summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/indexeddb
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/indexeddb')
-rw-r--r--Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp40
-rw-r--r--Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h22
-rw-r--r--Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl7
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h98
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBAny.cpp218
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBAny.h145
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCallbacks.h83
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursor.cpp508
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursor.h199
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursor.idl18
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp109
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackend.h98
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp86
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h88
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorDirection.h34
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl33
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp55
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h60
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl4
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabase.cpp626
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabase.h197
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabase.idl29
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp623
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h174
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h57
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp82
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h64
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseError.h16
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp12
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseException.h11
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp84
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h153
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h85
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp28
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h10
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactory.cpp213
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactory.h99
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactory.idl10
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp45
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h74
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp118
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetAllResult.h132
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetResult.cpp62
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetResult.h119
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBHistograms.h45
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndex.cpp451
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndex.h161
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndex.idl38
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h68
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKey.cpp115
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKey.h172
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyData.cpp446
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyData.h271
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp183
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyPath.h73
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp140
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRange.h70
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRange.idl16
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp79
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h69
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp969
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStore.h220
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStore.idl55
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h69
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp268
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h86
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl9
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp70
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h57
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h18
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequest.cpp755
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequest.h249
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequest.idl27
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp41
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h (renamed from Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h)28
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBServerConnection.h99
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransaction.cpp1492
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransaction.h375
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransaction.idl21
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp359
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h128
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp282
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h471
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp152
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h72
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionMode.h40
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl (renamed from Source/WebCore/Modules/indexeddb/IDBAny.idl)11
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBValue.cpp91
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBValue.h87
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp52
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h89
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl8
-rw-r--r--Source/WebCore/Modules/indexeddb/IndexedDB.h68
-rw-r--r--Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp72
-rw-r--r--Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h58
-rw-r--r--Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp44
-rw-r--r--Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h35
-rw-r--r--Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl33
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp562
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h177
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp446
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h155
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h98
-rw-r--r--Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp53
-rw-r--r--Source/WebCore/Modules/indexeddb/client/TransactionOperation.h270
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp202
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h100
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp1931
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h143
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp86
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp197
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h87
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp94
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h66
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp1807
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h367
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp626
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h100
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h106
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp192
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h92
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h82
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp420
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBSerialization.h45
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBServer.cpp682
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBServer.h148
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp232
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h102
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp419
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueStore.h118
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp296
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h107
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp66
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryCursor.h58
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp599
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h101
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp277
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndex.h112
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp228
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h61
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp522
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h137
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp356
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h74
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp2601
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h202
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp580
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h132
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp233
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h97
-rw-r--r--Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp91
-rw-r--r--Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h78
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp1908
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h281
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp236
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h102
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp375
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h105
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp114
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h137
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h67
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp178
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h111
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBError.cpp73
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBError.h87
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp51
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h (renamed from Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h)87
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp49
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h (renamed from Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h)65
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp70
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h103
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp47
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h78
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp163
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h115
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp160
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h175
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp108
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h150
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp244
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResultData.h231
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp121
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h130
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp451
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h131
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp (renamed from Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp)72
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IndexKey.h (renamed from Source/WebCore/Modules/indexeddb/IDBOperation.h)32
187 files changed, 24834 insertions, 13471 deletions
diff --git a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp
index 75e7a66a6..803844156 100644
--- a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp
+++ b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp
@@ -29,11 +29,10 @@
#if ENABLE(INDEXED_DATABASE)
#include "DOMWindow.h"
+#include "DatabaseProvider.h"
#include "Document.h"
#include "IDBFactory.h"
#include "Page.h"
-#include "PageGroupIndexedDatabase.h"
-#include "SecurityOrigin.h"
namespace WebCore {
@@ -56,22 +55,23 @@ DOMWindowIndexedDatabase* DOMWindowIndexedDatabase::from(DOMWindow* window)
{
DOMWindowIndexedDatabase* supplement = static_cast<DOMWindowIndexedDatabase*>(Supplement<DOMWindow>::from(window, supplementName()));
if (!supplement) {
- supplement = new DOMWindowIndexedDatabase(window);
- provideTo(window, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<DOMWindowIndexedDatabase>(window);
+ supplement = newSupplement.get();
+ provideTo(window, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
-void DOMWindowIndexedDatabase::disconnectFrameForPageCache()
+void DOMWindowIndexedDatabase::disconnectFrameForDocumentSuspension()
{
- m_suspendedIDBFactory = m_idbFactory.release();
- DOMWindowProperty::disconnectFrameForPageCache();
+ m_suspendedIDBFactory = WTFMove(m_idbFactory);
+ DOMWindowProperty::disconnectFrameForDocumentSuspension();
}
-void DOMWindowIndexedDatabase::reconnectFrameFromPageCache(Frame* frame)
+void DOMWindowIndexedDatabase::reconnectFrameFromDocumentSuspension(Frame* frame)
{
- DOMWindowProperty::reconnectFrameFromPageCache(frame);
- m_idbFactory = m_suspendedIDBFactory.release();
+ DOMWindowProperty::reconnectFrameFromDocumentSuspension(frame);
+ m_idbFactory = WTFMove(m_suspendedIDBFactory);
}
void DOMWindowIndexedDatabase::willDestroyGlobalObjectInCachedFrame()
@@ -92,26 +92,32 @@ void DOMWindowIndexedDatabase::willDetachGlobalObjectFromFrame()
DOMWindowProperty::willDetachGlobalObjectFromFrame();
}
-IDBFactory* DOMWindowIndexedDatabase::indexedDB(DOMWindow* window)
+IDBFactory* DOMWindowIndexedDatabase::indexedDB(DOMWindow& window)
{
- return from(window)->indexedDB();
+ return from(&window)->indexedDB();
}
IDBFactory* DOMWindowIndexedDatabase::indexedDB()
{
Document* document = m_window->document();
if (!document)
- return 0;
+ return nullptr;
Page* page = document->page();
if (!page)
- return 0;
+ return nullptr;
if (!m_window->isCurrentlyDisplayedInFrame())
- return 0;
+ return nullptr;
+
+ if (!m_idbFactory) {
+ auto* connectionProxy = document->idbConnectionProxy();
+ if (!connectionProxy)
+ return nullptr;
+
+ m_idbFactory = IDBFactory::create(*connectionProxy);
+ }
- if (!m_idbFactory)
- m_idbFactory = IDBFactory::create(PageGroupIndexedDatabase::from(page->group())->factoryBackend());
return m_idbFactory.get();
}
diff --git a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h
index 1d14b07d9..3de7de2e7 100644
--- a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h
+++ b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h
@@ -23,8 +23,7 @@
* DAMAGE.
*/
-#ifndef DOMWindowIndexedDatabase_h
-#define DOMWindowIndexedDatabase_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
@@ -37,21 +36,22 @@ class IDBFactory;
class DOMWindow;
class DOMWindowIndexedDatabase : public DOMWindowProperty, public Supplement<DOMWindow> {
+ WTF_MAKE_FAST_ALLOCATED;
public:
+ explicit DOMWindowIndexedDatabase(DOMWindow*);
virtual ~DOMWindowIndexedDatabase();
+
static DOMWindowIndexedDatabase* from(DOMWindow*);
- static IDBFactory* indexedDB(DOMWindow*);
+ WEBCORE_EXPORT static IDBFactory* indexedDB(DOMWindow&);
- virtual void disconnectFrameForPageCache() override;
- virtual void reconnectFrameFromPageCache(Frame*) override;
- virtual void willDestroyGlobalObjectInCachedFrame() override;
- virtual void willDestroyGlobalObjectInFrame() override;
- virtual void willDetachGlobalObjectFromFrame() override;
+ void disconnectFrameForDocumentSuspension() override;
+ void reconnectFrameFromDocumentSuspension(Frame*) override;
+ void willDestroyGlobalObjectInCachedFrame() override;
+ void willDestroyGlobalObjectInFrame() override;
+ void willDetachGlobalObjectFromFrame() override;
private:
- explicit DOMWindowIndexedDatabase(DOMWindow*);
-
IDBFactory* indexedDB();
static const char* supplementName();
@@ -63,5 +63,3 @@ private:
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // DOMWindowIndexedDatabase_h
diff --git a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl
index 19cfdb6b6..59f93d322 100644
--- a/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl
+++ b/Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* 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
@@ -27,8 +27,7 @@
[
Conditional=INDEXED_DATABASE,
] partial interface DOMWindow {
- [ImplementedAs=indexedDB] readonly attribute IDBFactory webkitIndexedDB;
-
readonly attribute IDBFactory indexedDB;
+ [ImplementedAs=indexedDB] readonly attribute IDBFactory webkitIndexedDB;
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h b/Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h
new file mode 100644
index 000000000..141b9383a
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#include "ActiveDOMObject.h"
+#include "ScriptExecutionContext.h"
+#include <wtf/CrossThreadTask.h>
+#include <wtf/MainThread.h>
+#include <wtf/Threading.h>
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+class IDBActiveDOMObject : public ActiveDOMObject {
+public:
+ ThreadIdentifier originThreadID() const { return m_originThreadID; }
+
+ void contextDestroyed() final {
+ ASSERT(currentThread() == m_originThreadID);
+
+ Locker<Lock> lock(m_scriptExecutionContextLock);
+ ActiveDOMObject::contextDestroyed();
+ }
+
+ template<typename T, typename... Parameters, typename... Arguments>
+ void performCallbackOnOriginThread(T& object, void (T::*method)(Parameters...), Arguments&&... arguments)
+ {
+ ASSERT(originThreadID() == object.originThreadID());
+
+ if (object.originThreadID() == currentThread()) {
+ (object.*method)(arguments...);
+ return;
+ }
+
+ Locker<Lock> lock(m_scriptExecutionContextLock);
+
+ ScriptExecutionContext* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ context->postCrossThreadTask(object, method, arguments...);
+ }
+
+ void callFunctionOnOriginThread(WTF::Function<void ()>&& function)
+ {
+ if (originThreadID() == currentThread()) {
+ function();
+ return;
+ }
+
+ Locker<Lock> lock(m_scriptExecutionContextLock);
+
+ ScriptExecutionContext* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ context->postTask(WTFMove(function));
+ }
+
+protected:
+ IDBActiveDOMObject(ScriptExecutionContext* context)
+ : ActiveDOMObject(context)
+ {
+ ASSERT(context);
+ }
+
+private:
+ ThreadIdentifier m_originThreadID { currentThread() };
+ Lock m_scriptExecutionContextLock;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBAny.cpp b/Source/WebCore/Modules/indexeddb/IDBAny.cpp
deleted file mode 100644
index 827a4cd2f..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBAny.cpp
+++ /dev/null
@@ -1,218 +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.
- *
- * 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 "IDBAny.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBCursorWithValue.h"
-#include "IDBDatabase.h"
-#include "IDBFactory.h"
-#include "IDBIndex.h"
-#include "IDBKeyPath.h"
-#include "IDBObjectStore.h"
-#include "DOMStringList.h"
-
-namespace WebCore {
-
-PassRefPtr<IDBAny> IDBAny::createInvalid()
-{
- return adoptRef(new IDBAny(UndefinedType));
-}
-
-PassRefPtr<IDBAny> IDBAny::createNull()
-{
- return adoptRef(new IDBAny(NullType));
-}
-
-PassRefPtr<IDBAny> IDBAny::createString(const String& value)
-{
- return adoptRef(new IDBAny(value));
-}
-
-IDBAny::IDBAny(Type type)
- : m_type(type)
- , m_integer(0)
-{
- ASSERT(type == UndefinedType || type == NullType);
-}
-
-IDBAny::~IDBAny()
-{
-}
-
-PassRefPtr<DOMStringList> IDBAny::domStringList()
-{
- ASSERT(m_type == DOMStringListType);
- return m_domStringList;
-}
-
-PassRefPtr<IDBCursor> IDBAny::idbCursor()
-{
- ASSERT(m_type == IDBCursorType);
- return m_idbCursor;
-}
-
-PassRefPtr<IDBCursorWithValue> IDBAny::idbCursorWithValue()
-{
- ASSERT(m_type == IDBCursorWithValueType);
- return m_idbCursorWithValue;
-}
-
-PassRefPtr<IDBDatabase> IDBAny::idbDatabase()
-{
- ASSERT(m_type == IDBDatabaseType);
- return m_idbDatabase;
-}
-
-PassRefPtr<IDBFactory> IDBAny::idbFactory()
-{
- ASSERT(m_type == IDBFactoryType);
- return m_idbFactory;
-}
-
-PassRefPtr<IDBIndex> IDBAny::idbIndex()
-{
- ASSERT(m_type == IDBIndexType);
- return m_idbIndex;
-}
-
-PassRefPtr<IDBObjectStore> IDBAny::idbObjectStore()
-{
- ASSERT(m_type == IDBObjectStoreType);
- return m_idbObjectStore;
-}
-
-PassRefPtr<IDBTransaction> IDBAny::idbTransaction()
-{
- ASSERT(m_type == IDBTransactionType);
- return m_idbTransaction;
-}
-
-const Deprecated::ScriptValue& IDBAny::scriptValue()
-{
- ASSERT(m_type == ScriptValueType);
- return m_scriptValue;
-}
-
-const String& IDBAny::string()
-{
- ASSERT(m_type == StringType);
- return m_string;
-}
-
-int64_t IDBAny::integer()
-{
- ASSERT(m_type == IntegerType);
- return m_integer;
-}
-
-IDBAny::IDBAny(PassRefPtr<DOMStringList> value)
- : m_type(DOMStringListType)
- , m_domStringList(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBCursorWithValue> value)
- : m_type(IDBCursorWithValueType)
- , m_idbCursorWithValue(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBCursor> value)
- : m_type(IDBCursorType)
- , m_idbCursor(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBDatabase> value)
- : m_type(IDBDatabaseType)
- , m_idbDatabase(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBFactory> value)
- : m_type(IDBFactoryType)
- , m_idbFactory(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBIndex> value)
- : m_type(IDBIndexType)
- , m_idbIndex(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBTransaction> value)
- : m_type(IDBTransactionType)
- , m_idbTransaction(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBObjectStore> value)
- : m_type(IDBObjectStoreType)
- , m_idbObjectStore(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(const Deprecated::ScriptValue& value)
- : m_type(ScriptValueType)
- , m_scriptValue(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(const IDBKeyPath& value)
- : m_type(KeyPathType)
- , m_idbKeyPath(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(const String& value)
- : m_type(StringType)
- , m_string(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(int64_t value)
- : m_type(IntegerType)
- , m_integer(value)
-{
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/indexeddb/IDBAny.h b/Source/WebCore/Modules/indexeddb/IDBAny.h
deleted file mode 100644
index d728456d6..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBAny.h
+++ /dev/null
@@ -1,145 +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.
- *
- * 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 IDBAny_h
-#define IDBAny_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBKeyPath.h"
-#include "ScriptWrappable.h"
-#include <bindings/ScriptValue.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class DOMStringList;
-class IDBCursor;
-class IDBCursorWithValue;
-class IDBDatabase;
-class IDBFactory;
-class IDBIndex;
-class IDBKeyPath;
-class IDBObjectStore;
-class IDBTransaction;
-
-class IDBAny : public ScriptWrappable, public RefCounted<IDBAny> {
-public:
- static PassRefPtr<IDBAny> createInvalid();
- static PassRefPtr<IDBAny> createNull();
- static PassRefPtr<IDBAny> createString(const String&);
- template<typename T>
- static PassRefPtr<IDBAny> create(T* idbObject)
- {
- return adoptRef(new IDBAny(idbObject));
- }
- template<typename T>
- static PassRefPtr<IDBAny> create(const T& idbObject)
- {
- return adoptRef(new IDBAny(idbObject));
- }
- template<typename T>
- static PassRefPtr<IDBAny> create(PassRefPtr<T> idbObject)
- {
- return adoptRef(new IDBAny(idbObject));
- }
- static PassRefPtr<IDBAny> create(int64_t value)
- {
- return adoptRef(new IDBAny(value));
- }
- ~IDBAny();
-
- enum Type {
- UndefinedType = 0,
- NullType,
- DOMStringListType,
- IDBCursorType,
- IDBCursorWithValueType,
- IDBDatabaseType,
- IDBFactoryType,
- IDBIndexType,
- IDBObjectStoreType,
- IDBTransactionType,
- ScriptValueType,
- IntegerType,
- StringType,
- KeyPathType,
- };
-
- Type type() const { return m_type; }
- // Use type() to figure out which one of these you're allowed to call.
- PassRefPtr<DOMStringList> domStringList();
- PassRefPtr<IDBCursor> idbCursor();
- PassRefPtr<IDBCursorWithValue> idbCursorWithValue();
- PassRefPtr<IDBDatabase> idbDatabase();
- PassRefPtr<IDBFactory> idbFactory();
- PassRefPtr<IDBIndex> idbIndex();
- PassRefPtr<IDBObjectStore> idbObjectStore();
- PassRefPtr<IDBTransaction> idbTransaction();
- const Deprecated::ScriptValue& scriptValue();
- int64_t integer();
- const String& string();
- const IDBKeyPath& keyPath() { return m_idbKeyPath; };
-
-private:
- explicit IDBAny(Type);
- explicit IDBAny(PassRefPtr<DOMStringList>);
- explicit IDBAny(PassRefPtr<IDBCursor>);
- explicit IDBAny(PassRefPtr<IDBCursorWithValue>);
- explicit IDBAny(PassRefPtr<IDBDatabase>);
- explicit IDBAny(PassRefPtr<IDBFactory>);
- explicit IDBAny(PassRefPtr<IDBIndex>);
- explicit IDBAny(PassRefPtr<IDBObjectStore>);
- explicit IDBAny(PassRefPtr<IDBTransaction>);
- explicit IDBAny(const IDBKeyPath&);
- explicit IDBAny(const String&);
- explicit IDBAny(const Deprecated::ScriptValue&);
- explicit IDBAny(int64_t);
-
- const Type m_type;
-
- // Only one of the following should ever be in use at any given time.
- const RefPtr<DOMStringList> m_domStringList;
- const RefPtr<IDBCursor> m_idbCursor;
- const RefPtr<IDBCursorWithValue> m_idbCursorWithValue;
- const RefPtr<IDBDatabase> m_idbDatabase;
- const RefPtr<IDBFactory> m_idbFactory;
- const RefPtr<IDBIndex> m_idbIndex;
- const RefPtr<IDBObjectStore> m_idbObjectStore;
- const RefPtr<IDBTransaction> m_idbTransaction;
- const IDBKeyPath m_idbKeyPath;
- const Deprecated::ScriptValue m_scriptValue;
- const String m_string;
- const int64_t m_integer;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBAny_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBCallbacks.h b/Source/WebCore/Modules/indexeddb/IDBCallbacks.h
deleted file mode 100644
index 15382bcb2..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBCallbacks.h
+++ /dev/null
@@ -1,83 +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 IDBCallbacks_h
-#define IDBCallbacks_h
-
-#include "IDBDatabaseError.h"
-#include "IDBKey.h"
-#include "IDBKeyPath.h"
-#include "SharedBuffer.h"
-#include <wtf/RefCounted.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-class DOMStringList;
-class IDBCursorBackend;
-class IDBDatabaseBackend;
-
-struct IDBDatabaseMetadata;
-
-class IDBCallbacks : public RefCounted<IDBCallbacks> {
-public:
- virtual ~IDBCallbacks() { }
-
- virtual void onError(PassRefPtr<IDBDatabaseError>) = 0;
- // From IDBFactory.webkitGetDatabaseNames()
- virtual void onSuccess(PassRefPtr<DOMStringList>) = 0;
- // From IDBObjectStore/IDBIndex.openCursor(), IDBIndex.openKeyCursor()
- virtual void onSuccess(PassRefPtr<IDBCursorBackend>, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>) = 0;
- // From IDBObjectStore.add()/put(), IDBIndex.getKey()
- virtual void onSuccess(PassRefPtr<IDBKey>) = 0;
- // From IDBObjectStore/IDBIndex.get()/count(), and various methods that yield null/undefined.
- virtual void onSuccess(PassRefPtr<SharedBuffer>) = 0;
- // From IDBObjectStore/IDBIndex.get() (with key injection)
- virtual void onSuccess(PassRefPtr<SharedBuffer>, PassRefPtr<IDBKey>, const IDBKeyPath&) = 0;
- // From IDBObjectStore/IDBIndex.count()
- virtual void onSuccess(int64_t value) = 0;
-
- // From IDBFactor.deleteDatabase(), IDBObjectStore/IDBIndex.get(), IDBObjectStore.delete(), IDBObjectStore.clear()
- virtual void onSuccess() = 0;
-
- // From IDBCursor.advance()/continue()
- virtual void onSuccess(PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>) = 0;
- // From IDBCursor.advance()/continue()
- virtual void onSuccessWithPrefetch(const Vector<RefPtr<IDBKey>>& keys, const Vector<RefPtr<IDBKey>>& primaryKeys, const Vector<RefPtr<SharedBuffer>>& values) = 0;
- // From IDBFactory.open()/deleteDatabase()
- virtual void onBlocked(uint64_t /* existingVersion */) { ASSERT_NOT_REACHED(); }
- // From IDBFactory.open()
- virtual void onUpgradeNeeded(uint64_t /* oldVersion */, PassRefPtr<IDBDatabaseBackend>, const IDBDatabaseMetadata&) { ASSERT_NOT_REACHED(); }
- virtual void onSuccess(PassRefPtr<IDBDatabaseBackend>, const IDBDatabaseMetadata&) { ASSERT_NOT_REACHED(); }
-};
-
-} // namespace WebCore
-
-#endif
-
-#endif // IDBCallbacks_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursor.cpp b/Source/WebCore/Modules/indexeddb/IDBCursor.cpp
index f6ea30ed5..7f490e986 100644
--- a/Source/WebCore/Modules/indexeddb/IDBCursor.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBCursor.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -28,309 +28,357 @@
#if ENABLE(INDEXED_DATABASE)
-#include "IDBAny.h"
+#include "ExceptionCode.h"
#include "IDBBindingUtilities.h"
-#include "IDBCallbacks.h"
-#include "IDBCursorBackend.h"
-#include "IDBKey.h"
+#include "IDBDatabase.h"
+#include "IDBDatabaseException.h"
+#include "IDBGetResult.h"
+#include "IDBIndex.h"
+#include "IDBIterateCursorData.h"
#include "IDBObjectStore.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
#include "Logging.h"
-#include "ScriptCallStack.h"
#include "ScriptExecutionContext.h"
-#include <limits>
+#include <heap/HeapInlines.h>
+#include <heap/StrongInlines.h>
+#include <runtime/JSCJSValueInlines.h>
+
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
+Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
{
- return adoptRef(new IDBCursor(backend, direction, request, source, transaction));
+ return adoptRef(*new IDBCursor(transaction, objectStore, info));
}
-const AtomicString& IDBCursor::directionNext()
+Ref<IDBCursor> IDBCursor::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
{
- DEFINE_STATIC_LOCAL(AtomicString, next, ("next", AtomicString::ConstructFromLiteral));
- return next;
+ return adoptRef(*new IDBCursor(transaction, index, info));
}
-const AtomicString& IDBCursor::directionNextUnique()
+IDBCursor::IDBCursor(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
+ : ActiveDOMObject(transaction.scriptExecutionContext())
+ , m_info(info)
+ , m_source(&objectStore)
{
- DEFINE_STATIC_LOCAL(AtomicString, nextunique, ("nextunique", AtomicString::ConstructFromLiteral));
- return nextunique;
-}
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
-const AtomicString& IDBCursor::directionPrev()
-{
- DEFINE_STATIC_LOCAL(AtomicString, prev, ("prev", AtomicString::ConstructFromLiteral));
- return prev;
+ suspendIfNeeded();
}
-const AtomicString& IDBCursor::directionPrevUnique()
+IDBCursor::IDBCursor(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
+ : ActiveDOMObject(transaction.scriptExecutionContext())
+ , m_info(info)
+ , m_source(&index)
{
- DEFINE_STATIC_LOCAL(AtomicString, prevunique, ("prevunique", AtomicString::ConstructFromLiteral));
- return prevunique;
-}
-
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
-IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
- : m_backend(backend)
- , m_request(request)
- , m_direction(direction)
- , m_source(source)
- , m_transaction(transaction)
- , m_transactionNotifier(transaction, this)
- , m_gotValue(false)
-{
- ASSERT(m_backend);
- ASSERT(m_request);
- ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
- ASSERT(m_transaction);
+ suspendIfNeeded();
}
IDBCursor::~IDBCursor()
{
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
}
-const String& IDBCursor::direction() const
+bool IDBCursor::sourcesDeleted() const
{
- LOG(StorageAPI, "IDBCursor::direction");
- return directionToString(m_direction);
-}
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
-const Deprecated::ScriptValue& IDBCursor::key() const
-{
- LOG(StorageAPI, "IDBCursor::key");
- return m_currentKeyValue;
+ return WTF::switchOn(m_source,
+ [] (const RefPtr<IDBObjectStore>& objectStore) { return objectStore->isDeleted(); },
+ [] (const RefPtr<IDBIndex>& index) { return index->isDeleted() || index->objectStore().isDeleted(); }
+ );
}
-const Deprecated::ScriptValue& IDBCursor::primaryKey() const
+IDBObjectStore& IDBCursor::effectiveObjectStore() const
{
- LOG(StorageAPI, "IDBCursor::primaryKey");
- return m_currentPrimaryKeyValue;
+ return WTF::switchOn(m_source,
+ [] (const RefPtr<IDBObjectStore>& objectStore) -> IDBObjectStore& { return *objectStore; },
+ [] (const RefPtr<IDBIndex>& index) -> IDBObjectStore& { return index->objectStore(); }
+ );
}
-const Deprecated::ScriptValue& IDBCursor::value() const
+IDBTransaction& IDBCursor::transaction() const
{
- LOG(StorageAPI, "IDBCursor::value");
- return m_currentValue;
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+ return effectiveObjectStore().transaction();
}
-IDBAny* IDBCursor::source() const
+ExceptionOr<Ref<IDBRequest>> IDBCursor::update(ExecState& state, JSValue value)
{
- return m_source.get();
-}
+ LOG(IndexedDB, "IDBCursor::update");
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
-PassRefPtr<IDBRequest> IDBCursor::update(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec)
-{
- LOG(StorageAPI, "IDBCursor::update");
+ if (sourcesDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
- if (!m_gotValue || isKeyCursor()) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (m_transaction->isReadOnly()) {
- ec = IDBDatabaseException::ReadOnlyError;
- return 0;
- }
+ if (!transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The transaction is inactive or finished.") };
+
+ if (transaction().isReadOnly())
+ return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The record may not be updated inside a read-only transaction.") };
- RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
- const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
- const bool usesInLineKeys = !keyPath.isNull();
+ if (!m_gotValue)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
+
+ if (!isKeyCursorWithValue())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The cursor is a key cursor.") };
+
+ auto& objectStore = effectiveObjectStore();
+ auto& optionalKeyPath = objectStore.info().keyPath();
+ const bool usesInLineKeys = !!optionalKeyPath;
if (usesInLineKeys) {
- RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath);
- if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, optionalKeyPath.value());
+ IDBKeyData keyPathKeyData(keyPathKey.get());
+ if (!keyPathKey || keyPathKeyData != m_currentPrimaryKeyData)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'update' on 'IDBCursor': The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.") };
}
- return objectStore->put(IDBDatabaseBackend::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, ec);
+ auto putResult = effectiveObjectStore().putForCursorUpdate(state, value, m_currentPrimaryKey.get());
+ if (putResult.hasException())
+ return putResult.releaseException();
+
+ auto request = putResult.releaseReturnValue();
+ request->setSource(*this);
+ ++m_outstandingRequestCount;
+
+ return WTFMove(request);
}
-void IDBCursor::advance(unsigned long count, ExceptionCode& ec)
+ExceptionOr<void> IDBCursor::advance(unsigned count)
{
- ec = 0;
- LOG(StorageAPI, "IDBCursor::advance");
- if (!m_gotValue) {
- ec = IDBDatabaseException::InvalidStateError;
- return;
- }
+ LOG(IndexedDB, "IDBCursor::advance");
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return;
- }
+ if (!m_request)
+ return Exception { IDBDatabaseException::InvalidStateError };
- if (!count) {
- ec = TypeError;
- return;
+ if (!count)
+ return Exception { TypeError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': A count argument with value 0 (zero) was supplied, must be greater than 0.") };
+
+ if (!transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The transaction is inactive or finished.") };
+
+ if (sourcesDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
+
+ if (!m_gotValue)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'advance' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
+
+ m_gotValue = false;
+
+ uncheckedIterateCursor(IDBKeyData(), count);
+
+ return { };
+}
+
+ExceptionOr<void> IDBCursor::continuePrimaryKey(ExecState& state, JSValue keyValue, JSValue primaryKeyValue)
+{
+ if (!transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The transaction is inactive or finished.") };
+
+ if (sourcesDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
+
+ if (!WTF::holds_alternative<RefPtr<IDBIndex>>(m_source))
+ return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's source is not an index.") };
+
+ auto direction = m_info.cursorDirection();
+ if (direction != IndexedDB::CursorDirection::Next && direction != IndexedDB::CursorDirection::Prev)
+ return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor's direction must be either \"next\" or \"prev\".") };
+
+ if (!m_gotValue)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
+
+ RefPtr<IDBKey> key = scriptValueToIDBKey(state, keyValue);
+ if (!key->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is not a valid key.") };
+
+ RefPtr<IDBKey> primaryKey = scriptValueToIDBKey(state, primaryKeyValue);
+ if (!primaryKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The second parameter is not a valid key.") };
+
+ IDBKeyData keyData = { key.get() };
+ IDBKeyData primaryKeyData = { primaryKey.get() };
+
+ if (keyData < m_currentKeyData && direction == IndexedDB::CursorDirection::Next)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is less than this cursor's position and this cursor's direction is \"next\".") };
+
+ if (keyData > m_currentKeyData && direction == IndexedDB::CursorDirection::Prev)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The first parameter is greater than this cursor's position and this cursor's direction is \"prev\".") };
+
+ if (keyData == m_currentKeyData) {
+ if (primaryKeyData <= m_currentPrimaryKeyData && direction == IndexedDB::CursorDirection::Next)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position less-than-or-equal-to this cursor's position and this cursor's direction is \"next\".") };
+ if (primaryKeyData >= m_currentPrimaryKeyData && direction == IndexedDB::CursorDirection::Prev)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continuePrimaryKey' on 'IDBCursor': The key parameters represent a position greater-than-or-equal-to this cursor's position and this cursor's direction is \"prev\".") };
}
- m_request->setPendingCursor(this);
m_gotValue = false;
- m_backend->advance(count, m_request, ec);
- ASSERT(!ec);
+
+ uncheckedIterateCursor(keyData, primaryKeyData);
+
+ return { };
}
-void IDBCursor::continueFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec)
+ExceptionOr<void> IDBCursor::continueFunction(ExecState& execState, JSValue keyValue)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
- continueFunction(key.release(), ec);
+ RefPtr<IDBKey> key;
+ if (!keyValue.isUndefined())
+ key = scriptValueToIDBKey(execState, keyValue);
+
+ return continueFunction(key.get());
}
-void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
+ExceptionOr<void> IDBCursor::continueFunction(const IDBKeyData& key)
{
- ec = 0;
- LOG(StorageAPI, "IDBCursor::continue");
- if (key && !key->isValid()) {
- ec = IDBDatabaseException::DataError;
- return;
- }
+ LOG(IndexedDB, "IDBCursor::continueFunction (to key %s)", key.loggingString().utf8().data());
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return;
- }
+ if (!m_request)
+ return Exception { IDBDatabaseException::InvalidStateError };
- if (!m_gotValue) {
- ec = IDBDatabaseException::InvalidStateError;
- return;
- }
+ if (!transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished.") };
+
+ if (sourcesDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
+
+ if (!m_gotValue)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
+
+ if (!key.isNull() && !key.isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key.") };
- if (key) {
- ASSERT(m_currentKey);
- if (m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::NextNoDuplicate) {
- if (!m_currentKey->isLessThan(key.get())) {
- ec = IDBDatabaseException::DataError;
- return;
- }
- } else {
- if (!key->isLessThan(m_currentKey.get())) {
- ec = IDBDatabaseException::DataError;
- return;
- }
- }
+ if (m_info.isDirectionForward()) {
+ if (!key.isNull() && key.compare(m_currentKeyData) <= 0)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position.") };
+ } else {
+ if (!key.isNull() && key.compare(m_currentKeyData) >= 0)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position.") };
}
- // FIXME: We're not using the context from when continue was called, which means the callback
- // will be on the original context openCursor was called on. Is this right?
- m_request->setPendingCursor(this);
m_gotValue = false;
- m_backend->continueFunction(key, m_request, ec);
- ASSERT(!ec);
+
+ uncheckedIterateCursor(key, 0);
+
+ return { };
}
-PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec)
+void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, unsigned count)
{
- ec = 0;
- LOG(StorageAPI, "IDBCursor::delete");
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (m_transaction->isReadOnly()) {
- ec = IDBDatabaseException::ReadOnlyError;
- return 0;
- }
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
- if (!m_gotValue || isKeyCursor()) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- m_backend->deleteFunction(request, ec);
- ASSERT(!ec);
- return request.release();
+ ++m_outstandingRequestCount;
+
+ m_request->willIterateCursor(*this);
+ transaction().iterateCursor(*this, { key, { }, count });
}
-void IDBCursor::postSuccessHandlerCallback()
+void IDBCursor::uncheckedIterateCursor(const IDBKeyData& key, const IDBKeyData& primaryKey)
{
- m_backend->postSuccessHandlerCallback();
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
+ ++m_outstandingRequestCount;
+
+ m_request->willIterateCursor(*this);
+ transaction().iterateCursor(*this, { key, primaryKey, 0 });
}
-void IDBCursor::close()
+ExceptionOr<Ref<WebCore::IDBRequest>> IDBCursor::deleteFunction(ExecState& state)
{
- m_transactionNotifier.cursorFinished();
- if (m_request) {
- m_request->finishCursor();
- m_request.clear();
- }
+ LOG(IndexedDB, "IDBCursor::deleteFunction");
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
+ if (sourcesDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor's source or effective object store has been deleted.") };
+
+ if (!transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The transaction is inactive or finished.") };
+
+ if (transaction().isReadOnly())
+ return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The record may not be deleted inside a read-only transaction.") };
+
+ if (!m_gotValue)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is being iterated or has iterated past its end.") };
+
+ if (!isKeyCursorWithValue())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBCursor': The cursor is a key cursor.") };
+
+ auto result = effectiveObjectStore().deleteFunction(state, m_currentPrimaryKey.get());
+ if (result.hasException())
+ return result.releaseException();
+
+ auto request = result.releaseReturnValue();
+ request->setSource(*this);
+ ++m_outstandingRequestCount;
+
+ return WTFMove(request);
}
-void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, Deprecated::ScriptValue& value)
+void IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
{
- m_currentKey = key;
- m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey);
-
- m_currentPrimaryKey = primaryKey;
- m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey);
-
- if (!isKeyCursor()) {
- RefPtr<IDBObjectStore> objectStore = effectiveObjectStore();
- const IDBObjectStoreMetadata metadata = objectStore->metadata();
- if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
-#ifndef NDEBUG
- RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, metadata.keyPath);
- ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get()));
-#endif
- bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath);
- // FIXME: There is no way to report errors here. Move this into onSuccessWithContinuation so that we can abort the transaction there. See: https://bugs.webkit.org/show_bug.cgi?id=92278
- ASSERT_UNUSED(injected, injected);
- }
+ LOG(IndexedDB, "IDBCursor::setGetResult - current key %s", getResult.keyData().loggingString().substring(0, 100).utf8().data());
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
+
+ auto* context = request.scriptExecutionContext();
+ if (!context)
+ return;
+
+ auto* exec = context->execState();
+ if (!exec)
+ return;
+
+ if (!getResult.isDefined()) {
+ m_currentKey = { };
+ m_currentKeyData = { };
+ m_currentPrimaryKey = { };
+ m_currentPrimaryKeyData = { };
+ m_currentValue = { };
+
+ m_gotValue = false;
+ return;
}
- m_currentValue = value;
+
+ auto& vm = context->vm();
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBCursor wrapper can be used, rather than the lexicalGlobalObject.
+ m_currentKey = { vm, toJS(*exec, *exec->lexicalGlobalObject(), getResult.keyData().maybeCreateIDBKey().get()) };
+ m_currentKeyData = getResult.keyData();
+ m_currentPrimaryKey = { vm, toJS(*exec, *exec->lexicalGlobalObject(), getResult.primaryKeyData().maybeCreateIDBKey().get()) };
+ m_currentPrimaryKeyData = getResult.primaryKeyData();
+
+ if (isKeyCursorWithValue())
+ m_currentValue = { vm, deserializeIDBValueToJSValue(*exec, getResult.value()) };
+ else
+ m_currentValue = { };
m_gotValue = true;
}
-PassRefPtr<IDBObjectStore> IDBCursor::effectiveObjectStore()
+const char* IDBCursor::activeDOMObjectName() const
{
- if (m_source->type() == IDBAny::IDBObjectStoreType)
- return m_source->idbObjectStore();
- RefPtr<IDBIndex> index = m_source->idbIndex();
- return index->objectStore();
+ return "IDBCursor";
}
-IndexedDB::CursorDirection IDBCursor::stringToDirection(const String& directionString, ExceptionCode& ec)
+bool IDBCursor::canSuspendForDocumentSuspension() const
{
- if (directionString == IDBCursor::directionNext())
- return IndexedDB::CursorDirection::Next;
- if (directionString == IDBCursor::directionNextUnique())
- return IndexedDB::CursorDirection::NextNoDuplicate;
- if (directionString == IDBCursor::directionPrev())
- return IndexedDB::CursorDirection::Prev;
- if (directionString == IDBCursor::directionPrevUnique())
- return IndexedDB::CursorDirection::PrevNoDuplicate;
-
- ec = TypeError;
- return IndexedDB::CursorDirection::Next;
+ return false;
}
-const AtomicString& IDBCursor::directionToString(IndexedDB::CursorDirection direction)
+bool IDBCursor::hasPendingActivity() const
{
- switch (direction) {
- case IndexedDB::CursorDirection::Next:
- return IDBCursor::directionNext();
-
- case IndexedDB::CursorDirection::NextNoDuplicate:
- return IDBCursor::directionNextUnique();
-
- case IndexedDB::CursorDirection::Prev:
- return IDBCursor::directionPrev();
-
- case IndexedDB::CursorDirection::PrevNoDuplicate:
- return IDBCursor::directionPrevUnique();
+ return m_outstandingRequestCount;
+}
- default:
- ASSERT_NOT_REACHED();
- return IDBCursor::directionNext();
- }
+void IDBCursor::decrementOutstandingRequestCount()
+{
+ ASSERT(m_outstandingRequestCount);
+ --m_outstandingRequestCount;
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursor.h b/Source/WebCore/Modules/indexeddb/IDBCursor.h
index b58d5aee9..5c48e70ad 100644
--- a/Source/WebCore/Modules/indexeddb/IDBCursor.h
+++ b/Source/WebCore/Modules/indexeddb/IDBCursor.h
@@ -1,111 +1,142 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBCursor_h
-#define IDBCursor_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
-#include "IDBKey.h"
-#include "IDBTransaction.h"
-#include "IndexedDB.h"
-#include "ScriptWrappable.h"
-#include <bindings/ScriptValue.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
+#include "ActiveDOMObject.h"
+#include "DOMWrapperWorld.h"
+#include "ExceptionOr.h"
+#include "IDBCursorDirection.h"
+#include "IDBCursorInfo.h"
+#include <heap/Strong.h>
+#include <wtf/Variant.h>
namespace WebCore {
-class DOMRequestState;
-class IDBAny;
-class IDBCallbacks;
-class IDBCursorBackend;
-class IDBRequest;
-class ScriptExecutionContext;
-
-typedef int ExceptionCode;
+class IDBGetResult;
+class IDBIndex;
+class IDBObjectStore;
+class IDBTransaction;
-class IDBCursor : public ScriptWrappable, public RefCounted<IDBCursor> {
+class IDBCursor : public ScriptWrappable, public RefCounted<IDBCursor>, public ActiveDOMObject {
public:
- static const AtomicString& directionNext();
- static const AtomicString& directionNextUnique();
- static const AtomicString& directionPrev();
- static const AtomicString& directionPrevUnique();
+ static Ref<IDBCursor> create(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&);
+ static Ref<IDBCursor> create(IDBTransaction&, IDBIndex&, const IDBCursorInfo&);
+
+ virtual ~IDBCursor();
- static IndexedDB::CursorDirection stringToDirection(const String& modeString, ExceptionCode&);
- static const AtomicString& directionToString(IndexedDB::CursorDirection mode);
+ using Source = Variant<RefPtr<IDBObjectStore>, RefPtr<IDBIndex>>;
- static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackend>, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*);
- virtual ~IDBCursor();
+ const Source& source() const;
+ IDBCursorDirection direction() const;
+ JSC::JSValue key() const;
+ JSC::JSValue primaryKey() const;
+ JSC::JSValue value() const;
+
+ ExceptionOr<Ref<IDBRequest>> update(JSC::ExecState&, JSC::JSValue);
+ ExceptionOr<void> advance(unsigned);
+ ExceptionOr<void> continueFunction(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<void> continuePrimaryKey(JSC::ExecState&, JSC::JSValue key, JSC::JSValue primaryKey);
+ ExceptionOr<Ref<IDBRequest>> deleteFunction(JSC::ExecState&);
+
+ ExceptionOr<void> continueFunction(const IDBKeyData&);
+
+ const IDBCursorInfo& info() const { return m_info; }
- // Implement the IDL
- const String& direction() const;
- const Deprecated::ScriptValue& key() const;
- const Deprecated::ScriptValue& primaryKey() const;
- const Deprecated::ScriptValue& value() const;
- IDBAny* source() const;
-
- PassRefPtr<IDBRequest> update(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&);
- void advance(unsigned long, ExceptionCode&);
- // FIXME: Try to modify the code generator so this overload is unneeded.
- void continueFunction(ScriptExecutionContext*, ExceptionCode& ec) { continueFunction(static_cast<IDBKey*>(0), ec); }
- void continueFunction(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, ExceptionCode&);
-
- void continueFunction(PassRefPtr<IDBKey>, ExceptionCode&);
- void postSuccessHandlerCallback();
- void close();
- void setValueReady(DOMRequestState*, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, Deprecated::ScriptValue&);
- PassRefPtr<IDBKey> idbPrimaryKey() { return m_currentPrimaryKey; }
+ void setRequest(IDBRequest& request) { m_request = &request; }
+ void clearRequest() { m_request = nullptr; }
+ IDBRequest* request() { return m_request; }
+
+ void setGetResult(IDBRequest&, const IDBGetResult&);
+
+ virtual bool isKeyCursorWithValue() const { return false; }
+
+ void decrementOutstandingRequestCount();
+
+ bool hasPendingActivity() const final;
protected:
- IDBCursor(PassRefPtr<IDBCursorBackend>, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*);
- virtual bool isKeyCursor() const { return true; }
+ IDBCursor(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&);
+ IDBCursor(IDBTransaction&, IDBIndex&, const IDBCursorInfo&);
private:
- PassRefPtr<IDBObjectStore> effectiveObjectStore();
-
- RefPtr<IDBCursorBackend> m_backend;
- RefPtr<IDBRequest> m_request;
- const IndexedDB::CursorDirection m_direction;
- RefPtr<IDBAny> m_source;
- RefPtr<IDBTransaction> m_transaction;
- IDBTransaction::OpenCursorNotifier m_transactionNotifier;
- bool m_gotValue;
- // These values are held because m_backend may advance while they
- // are still valid for the current success handlers.
- Deprecated::ScriptValue m_currentKeyValue;
- Deprecated::ScriptValue m_currentPrimaryKeyValue;
- RefPtr<IDBKey> m_currentKey;
- RefPtr<IDBKey> m_currentPrimaryKey;
- Deprecated::ScriptValue m_currentValue;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
+
+ bool sourcesDeleted() const;
+ IDBObjectStore& effectiveObjectStore() const;
+ IDBTransaction& transaction() const;
+
+ void uncheckedIterateCursor(const IDBKeyData&, unsigned count);
+ void uncheckedIterateCursor(const IDBKeyData&, const IDBKeyData&);
+
+ // Cursors are created with an outstanding iteration request.
+ unsigned m_outstandingRequestCount { 1 };
+
+ IDBCursorInfo m_info;
+ Source m_source;
+ IDBRequest* m_request { nullptr };
+
+ bool m_gotValue { false };
+
+ IDBKeyData m_currentKeyData;
+ IDBKeyData m_currentPrimaryKeyData;
+
+ JSC::Strong<JSC::Unknown> m_currentKey;
+ JSC::Strong<JSC::Unknown> m_currentPrimaryKey;
+ JSC::Strong<JSC::Unknown> m_currentValue;
};
-} // namespace WebCore
-#endif
+inline const IDBCursor::Source& IDBCursor::source() const
+{
+ return m_source;
+}
+
+inline IDBCursorDirection IDBCursor::direction() const
+{
+ return m_info.cursorDirection();
+}
+
+inline JSC::JSValue IDBCursor::key() const
+{
+ return m_currentKey.get();
+}
+
+inline JSC::JSValue IDBCursor::primaryKey() const
+{
+ return m_currentPrimaryKey.get();
+}
+
+inline JSC::JSValue IDBCursor::value() const
+{
+ return m_currentValue.get();
+}
+
+} // namespace WebCore
-#endif // IDBCursor_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursor.idl b/Source/WebCore/Modules/indexeddb/IDBCursor.idl
index e34ec827b..867fa46c4 100644
--- a/Source/WebCore/Modules/indexeddb/IDBCursor.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBCursor.idl
@@ -24,16 +24,20 @@
*/
[
+ ActiveDOMObject,
Conditional=INDEXED_DATABASE,
- JSNoStaticTables,
+ CustomToJSObject,
+ JSCustomMarkFunction,
+ SkipVTableValidation,
] interface IDBCursor {
- readonly attribute IDBAny source;
- readonly attribute DOMString direction;
+ readonly attribute (IDBObjectStore or IDBIndex) source;
+ readonly attribute IDBCursorDirection direction;
readonly attribute any key;
readonly attribute any primaryKey;
- [CallWith=ScriptState, RaisesException] IDBRequest update(any value);
- [RaisesException] void advance([EnforceRange] unsigned long count);
- [CallWith=ScriptExecutionContext, ImplementedAs=continueFunction, RaisesException] void continue(optional any key);
- [CallWith=ScriptExecutionContext, ImplementedAs=deleteFunction, RaisesException] IDBRequest delete();
+ [CallWith=ScriptState, MayThrowException] IDBRequest update(any value);
+ [MayThrowException] void advance([EnforceRange] unsigned long count);
+ [CallWith=ScriptState, ImplementedAs=continueFunction, MayThrowException] void continue(optional any key);
+ [CallWith=ScriptState, MayThrowException] void continuePrimaryKey(any key, any primaryKey);
+ [CallWith=ScriptState, ImplementedAs=deleteFunction, MayThrowException] IDBRequest delete();
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp b/Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp
deleted file mode 100644
index 7bb600b2b..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp
+++ /dev/null
@@ -1,109 +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.
- *
- * 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 "IDBCursorBackend.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBCallbacks.h"
-#include "IDBCursorBackendOperations.h"
-#include "IDBDatabaseBackend.h"
-#include "IDBDatabaseCallbacks.h"
-#include "IDBDatabaseError.h"
-#include "IDBDatabaseException.h"
-#include "IDBKeyRange.h"
-#include "IDBOperation.h"
-#include "IDBServerConnection.h"
-#include "Logging.h"
-#include "SharedBuffer.h"
-
-namespace WebCore {
-
-IDBCursorBackend::IDBCursorBackend(int64_t cursorID, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, IDBTransactionBackend& transaction, int64_t objectStoreID)
- : m_taskType(taskType)
- , m_cursorType(cursorType)
- , m_transaction(&transaction)
- , m_objectStoreID(objectStoreID)
- , m_cursorID(cursorID)
- , m_savedCursorID(0)
- , m_closed(false)
-{
- m_transaction->registerOpenCursor(this);
-}
-
-IDBCursorBackend::~IDBCursorBackend()
-{
- m_transaction->unregisterOpenCursor(this);
-}
-
-void IDBCursorBackend::continueFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode&)
-{
- LOG(StorageAPI, "IDBCursorBackend::continue");
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- m_transaction->scheduleTask(m_taskType, CursorIterationOperation::create(this, key, callbacks));
-}
-
-void IDBCursorBackend::advance(unsigned long count, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode&)
-{
- LOG(StorageAPI, "IDBCursorBackend::advance");
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- m_transaction->scheduleTask(CursorAdvanceOperation::create(this, count, callbacks));
-}
-
-void IDBCursorBackend::deleteFunction(PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode&)
-{
- LOG(StorageAPI, "IDBCursorBackend::delete");
- ASSERT(m_transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::create(primaryKey());
- m_transaction->database().deleteRange(m_transaction->id(), m_objectStoreID, keyRange.release(), prpCallbacks);
-}
-
-void IDBCursorBackend::close()
-{
- LOG(StorageAPI, "IDBCursorBackend::close");
- clear();
- m_closed = true;
- m_savedCursorID = 0;
-}
-
-void IDBCursorBackend::updateCursorData(IDBKey* key, IDBKey* primaryKey, SharedBuffer* value)
-{
- m_currentKey = key;
- m_currentPrimaryKey = primaryKey;
- m_currentValue = value;
-}
-
-void IDBCursorBackend::clear()
-{
- m_cursorID = 0;
- m_currentKey = 0;
- m_currentPrimaryKey = 0;
- m_currentValue = 0;
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.h b/Source/WebCore/Modules/indexeddb/IDBCursorBackend.h
deleted file mode 100644
index 3ee5b94a4..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBCursorBackend.h
+++ /dev/null
@@ -1,98 +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.
- *
- * 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 IDBCursorBackend_h
-#define IDBCursorBackend_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBDatabaseBackend.h"
-#include "IDBTransactionBackend.h"
-#include "SharedBuffer.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBKeyRange;
-
-class IDBCursorBackend : public RefCounted<IDBCursorBackend> {
-public:
- static PassRefPtr<IDBCursorBackend> create(int64_t cursorID, IndexedDB::CursorType cursorType, IDBTransactionBackend& transaction, int64_t objectStoreID)
- {
- return adoptRef(new IDBCursorBackend(cursorID, cursorType, IDBDatabaseBackend::NormalTask, transaction, objectStoreID));
- }
- static PassRefPtr<IDBCursorBackend> create(int64_t cursorID, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, IDBTransactionBackend& transaction, int64_t objectStoreID)
- {
- return adoptRef(new IDBCursorBackend(cursorID, cursorType, taskType, transaction, objectStoreID));
- }
- ~IDBCursorBackend();
-
- void advance(unsigned long, PassRefPtr<IDBCallbacks>, ExceptionCode&);
- void continueFunction(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, ExceptionCode&);
- void deleteFunction(PassRefPtr<IDBCallbacks>, ExceptionCode&);
- void postSuccessHandlerCallback() { }
-
- IDBKey* key() const { return m_currentKey.get(); }
- IDBKey* primaryKey() const { return m_currentPrimaryKey.get(); }
- SharedBuffer* value() const { return (m_cursorType == IndexedDB::CursorType::KeyOnly) ? 0 : m_currentValue.get(); }
- void updateCursorData(IDBKey*, IDBKey* primaryKey, SharedBuffer* value);
-
- void close();
-
- IndexedDB::CursorType cursorType() const { return m_cursorType; }
-
- int64_t id() const { return m_cursorID; }
-
- IDBTransactionBackend& transaction() { return *m_transaction; }
-
- void clear();
- void setSavedCursorID(int64_t cursorID) { m_savedCursorID = cursorID; }
-
-private:
- IDBCursorBackend(int64_t cursorID, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, IDBTransactionBackend&, int64_t objectStoreID);
-
- IDBDatabaseBackend::TaskType m_taskType;
- IndexedDB::CursorType m_cursorType;
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
-
- int64_t m_cursorID;
- int64_t m_savedCursorID;
-
- RefPtr<IDBKey> m_currentKey;
- RefPtr<IDBKey> m_currentPrimaryKey;
- RefPtr<SharedBuffer> m_currentValue;
-
- bool m_closed;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBCursorBackend_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp b/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp
deleted file mode 100644
index 9860745dc..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple 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.
- *
- * 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 "IDBCursorBackendOperations.h"
-
-#include "IDBServerConnection.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "Logging.h"
-
-namespace WebCore {
-
-void CursorAdvanceOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "CursorAdvanceOperation");
-
- RefPtr<CursorAdvanceOperation> operation(this);
- auto callback = [this, operation, completionCallback](PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBDatabaseError> error) {
- if (error) {
- m_cursor->clear();
- // FIXME: The LevelDB backend calls onSuccess even on failure.
- // This will probably have to change soon (for sanity) and will probably break LevelDB
- m_callbacks->onSuccess(static_cast<SharedBuffer*>(0));
- } else {
- m_cursor->updateCursorData(key.get(), primaryKey.get(), value.get());
- m_callbacks->onSuccess(key, primaryKey, value);
- }
-
- // FIXME: Cursor operations should be able to pass along an error instead of success
- completionCallback();
- };
-
- m_cursor->transaction().database().serverConnection().cursorAdvance(*m_cursor, *this, callback);
-}
-
-void CursorIterationOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "CursorIterationOperation");
-
- RefPtr<CursorIterationOperation> operation(this);
- auto callback = [this, operation, completionCallback](PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBDatabaseError> error) {
- if (error) {
- m_cursor->clear();
- // FIXME: The LevelDB backend calls onSuccess even on failure.
- // This will probably have to change soon (for sanity) and will probably break LevelDB
- m_callbacks->onSuccess(static_cast<SharedBuffer*>(0));
- } else {
- m_cursor->updateCursorData(key.get(), primaryKey.get(), value.get());
- m_callbacks->onSuccess(key, primaryKey, value);
- }
-
- // FIXME: Cursor operations should be able to pass along an error instead of success
- completionCallback();
- };
-
- m_cursor->transaction().database().serverConnection().cursorIterate(*m_cursor, *this, callback);
-}
-
-} // namespace WebCore
-
-#endif // ENABLED(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h b/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h
deleted file mode 100644
index 467f1deb4..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple 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.
- *
- * 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 IDBCursorBackendOperations_h
-#define IDBCursorBackendOperations_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBCursorBackend.h"
-#include "IDBOperation.h"
-
-namespace WebCore {
-
-class CursorIterationOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(PassRefPtr<IDBCursorBackend> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new CursorIterationOperation(cursor, key, callbacks));
- }
- virtual void perform(std::function<void()> completionCallback) override final;
-
- int64_t cursorID() const { return m_cursor->id(); }
- IDBKey* key() const { return m_key.get(); }
-
-private:
- CursorIterationOperation(PassRefPtr<IDBCursorBackend> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
- : m_cursor(cursor)
- , m_key(key)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBCursorBackend> m_cursor;
- RefPtr<IDBKey> m_key;
- RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class CursorAdvanceOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(PassRefPtr<IDBCursorBackend> cursor, unsigned long count, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new CursorAdvanceOperation(cursor, count, callbacks));
- }
- virtual void perform(std::function<void()> completionCallback) override final;
-
- int64_t cursorID() const { return m_cursor->id(); }
- unsigned long count() const { return m_count; }
-
-private:
- CursorAdvanceOperation(PassRefPtr<IDBCursorBackend> cursor, unsigned long count, PassRefPtr<IDBCallbacks> callbacks)
- : m_cursor(cursor)
- , m_count(count)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBCursorBackend> m_cursor;
- unsigned long m_count;
- RefPtr<IDBCallbacks> m_callbacks;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBCursorBackendOperations_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorDirection.h b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.h
new file mode 100644
index 000000000..e40fb8630
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#include "IndexedDB.h"
+
+namespace WebCore {
+
+using IDBCursorDirection = IndexedDB::CursorDirection;
+
+}
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl
new file mode 100644
index 000000000..b85fdc0f4
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+[
+ Conditional=INDEXED_DATABASE,
+] enum IDBCursorDirection {
+ "next",
+ "nextunique",
+ "prev",
+ "prevunique"
+};
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp
index 90ec57af5..603f1b2fc 100644
--- a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -28,24 +28,27 @@
#if ENABLE(INDEXED_DATABASE)
-#include "IDBCursorBackend.h"
-#include "IDBKey.h"
+#include <heap/HeapInlines.h>
namespace WebCore {
-PassRefPtr<IDBCursorWithValue> IDBCursorWithValue::create(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
+Ref<IDBCursorWithValue> IDBCursorWithValue::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
+{
+ return adoptRef(*new IDBCursorWithValue(transaction, objectStore, info));
+}
+
+Ref<IDBCursorWithValue> IDBCursorWithValue::create(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
{
- return adoptRef(new IDBCursorWithValue(backend, direction, request, source, transaction));
+ return adoptRef(*new IDBCursorWithValue(transaction, index, info));
}
-PassRefPtr<IDBCursorWithValue> IDBCursorWithValue::fromCursor(PassRefPtr<IDBCursor> prpCursor)
+IDBCursorWithValue::IDBCursorWithValue(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
+ : IDBCursor(transaction, objectStore, info)
{
- RefPtr<IDBCursorWithValue> cursorWithValue(static_cast<IDBCursorWithValue*>(prpCursor.get()));
- return cursorWithValue.release();
}
-IDBCursorWithValue::IDBCursorWithValue(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
- : IDBCursor(backend, direction, request, source, transaction)
+IDBCursorWithValue::IDBCursorWithValue(IDBTransaction& transaction, IDBIndex& index, const IDBCursorInfo& info)
+ : IDBCursor(transaction, index, info)
{
}
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h
index 96f96b0e1..f78891b7d 100644
--- a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h
+++ b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h
@@ -1,55 +1,55 @@
/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBCursorWithValue_h
-#define IDBCursorWithValue_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
#include "IDBCursor.h"
+#include <wtf/TypeCasts.h>
namespace WebCore {
-class IDBCursorWithValue : public IDBCursor {
+class IDBCursorWithValue final : public IDBCursor {
public:
- static PassRefPtr<IDBCursorWithValue> create(PassRefPtr<IDBCursorBackend>, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*);
- static PassRefPtr<IDBCursorWithValue> fromCursor(PassRefPtr<IDBCursor>);
- virtual ~IDBCursorWithValue();
+ static Ref<IDBCursorWithValue> create(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&);
+ static Ref<IDBCursorWithValue> create(IDBTransaction&, IDBIndex&, const IDBCursorInfo&);
- // The value attribute defined in the IDL is simply implemented in IDBCursor (but not exposed via
- // its IDL). This is to make the implementation more simple while matching what the spec says.
+ virtual ~IDBCursorWithValue();
-protected:
- virtual bool isKeyCursor() const override { return false; }
+ bool isKeyCursorWithValue() const override { return true; }
private:
- IDBCursorWithValue(PassRefPtr<IDBCursorBackend>, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*);
+ IDBCursorWithValue(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&);
+ IDBCursorWithValue(IDBTransaction&, IDBIndex&, const IDBCursorInfo&);
};
} // namespace WebCore
-#endif
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::IDBCursorWithValue)
+ static bool isType(const WebCore::IDBCursor& cursor) { return cursor.isKeyCursorWithValue(); }
+SPECIALIZE_TYPE_TRAITS_END()
-#endif // IDBCursorWithValue_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl
index 7ee92da02..422a1bcd4 100644
--- a/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl
@@ -25,7 +25,9 @@
[
Conditional=INDEXED_DATABASE,
- JSNoStaticTables
+ ActiveDOMObject,
+ SkipVTableValidation,
+ JSCustomMarkFunction,
] interface IDBCursorWithValue : IDBCursor {
readonly attribute any value;
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp
index e8ca31c29..fad585ca1 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabase.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -29,358 +29,482 @@
#if ENABLE(INDEXED_DATABASE)
#include "DOMStringList.h"
+#include "EventNames.h"
#include "EventQueue.h"
#include "ExceptionCode.h"
-#include "HistogramSupport.h"
-#include "IDBAny.h"
-#include "IDBDatabaseCallbacks.h"
-#include "IDBDatabaseError.h"
+#include "IDBConnectionProxy.h"
+#include "IDBConnectionToServer.h"
#include "IDBDatabaseException.h"
-#include "IDBEventDispatcher.h"
-#include "IDBHistograms.h"
#include "IDBIndex.h"
-#include "IDBKeyPath.h"
#include "IDBObjectStore.h"
+#include "IDBOpenDBRequest.h"
+#include "IDBResultData.h"
#include "IDBTransaction.h"
#include "IDBVersionChangeEvent.h"
#include "Logging.h"
-#include "ScriptCallStack.h"
#include "ScriptExecutionContext.h"
-#include <atomic>
-#include <limits>
-#include <wtf/Atomics.h>
+#include <heap/HeapInlines.h>
namespace WebCore {
-PassRefPtr<IDBDatabase> IDBDatabase::create(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackend> database, PassRefPtr<IDBDatabaseCallbacks> callbacks)
+Ref<IDBDatabase> IDBDatabase::create(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBResultData& resultData)
{
- RefPtr<IDBDatabase> idbDatabase(adoptRef(new IDBDatabase(context, database, callbacks)));
- idbDatabase->suspendIfNeeded();
- return idbDatabase.release();
+ return adoptRef(*new IDBDatabase(context, connectionProxy, resultData));
}
-IDBDatabase::IDBDatabase(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackend> backend, PassRefPtr<IDBDatabaseCallbacks> callbacks)
- : ActiveDOMObject(context)
- , m_backend(backend)
- , m_closePending(false)
- , m_contextStopped(false)
- , m_databaseCallbacks(callbacks)
+IDBDatabase::IDBDatabase(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBResultData& resultData)
+ : IDBActiveDOMObject(&context)
+ , m_connectionProxy(connectionProxy)
+ , m_info(resultData.databaseInfo())
+ , m_databaseConnectionIdentifier(resultData.databaseConnectionIdentifier())
+ , m_eventNames(eventNames())
{
- // We pass a reference of this object before it can be adopted.
- relaxAdoptionRequirement();
+ LOG(IndexedDB, "IDBDatabase::IDBDatabase - Creating database %s with version %" PRIu64 " connection %" PRIu64 " (%p)", m_info.name().utf8().data(), m_info.version(), m_databaseConnectionIdentifier, this);
+ suspendIfNeeded();
+ m_connectionProxy->registerDatabaseConnection(*this);
}
IDBDatabase::~IDBDatabase()
{
- // This does what IDBDatabase::close does, but without any ref/deref of the
- // database since it is already in the process of being deleted. The logic here
- // is also simpler since we know there are no transactions (since they ref the
- // database when they are alive).
+ ASSERT(currentThread() == originThreadID());
- ASSERT(m_transactions.isEmpty());
+ if (!m_closedInServer)
+ m_connectionProxy->databaseConnectionClosed(*this);
- if (!m_closePending) {
- m_closePending = true;
- m_backend->close(m_databaseCallbacks);
- }
+ m_connectionProxy->unregisterDatabaseConnection(*this);
+}
- if (auto* context = scriptExecutionContext()) {
- // Remove any pending versionchange events scheduled to fire on this
- // connection. They would have been scheduled by the backend when another
- // connection called setVersion, but the frontend connection is being
- // closed before they could fire.
- for (auto& event : m_enqueuedEvents)
- context->eventQueue().cancelEvent(*event);
- }
+bool IDBDatabase::hasPendingActivity() const
+{
+ ASSERT(currentThread() == originThreadID() || mayBeGCThread());
+
+ if (m_closedInServer)
+ return false;
+
+ if (!m_activeTransactions.isEmpty() || !m_committingTransactions.isEmpty() || !m_abortingTransactions.isEmpty())
+ return true;
+
+ return hasEventListeners(m_eventNames.abortEvent) || hasEventListeners(m_eventNames.errorEvent) || hasEventListeners(m_eventNames.versionchangeEvent);
}
-int64_t IDBDatabase::nextTransactionId()
+const String IDBDatabase::name() const
{
- // Only keep a 32-bit counter to allow ports to use the other 32
- // bits of the id.
- static std::atomic<uint32_t> currentTransactionId;
+ ASSERT(currentThread() == originThreadID());
+ return m_info.name();
+}
- return ++currentTransactionId;
+uint64_t IDBDatabase::version() const
+{
+ ASSERT(currentThread() == originThreadID());
+ return m_info.version();
}
-void IDBDatabase::transactionCreated(IDBTransaction* transaction)
+RefPtr<DOMStringList> IDBDatabase::objectStoreNames() const
{
- ASSERT(transaction);
- ASSERT(!m_transactions.contains(transaction->id()));
- m_transactions.add(transaction->id(), transaction);
+ ASSERT(currentThread() == originThreadID());
- if (transaction->isVersionChange()) {
- ASSERT(!m_versionChangeTransaction);
- m_versionChangeTransaction = transaction;
- }
+ RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
+ for (auto& name : m_info.objectStoreNames())
+ objectStoreNames->append(name);
+ objectStoreNames->sort();
+ return objectStoreNames;
}
-void IDBDatabase::transactionFinished(IDBTransaction* transaction)
+void IDBDatabase::renameObjectStore(IDBObjectStore& objectStore, const String& newName)
{
- ASSERT(transaction);
- ASSERT(m_transactions.contains(transaction->id()));
- ASSERT(m_transactions.get(transaction->id()) == transaction);
- m_transactions.remove(transaction->id());
-
- if (transaction->isVersionChange()) {
- ASSERT(m_versionChangeTransaction == transaction);
- m_versionChangeTransaction = 0;
- }
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_versionChangeTransaction);
+ ASSERT(m_info.hasObjectStore(objectStore.info().name()));
+
+ m_info.renameObjectStore(objectStore.info().identifier(), newName);
- if (m_closePending && m_transactions.isEmpty())
- closeConnection();
+ m_versionChangeTransaction->renameObjectStore(objectStore, newName);
}
-void IDBDatabase::onAbort(int64_t transactionId, PassRefPtr<IDBDatabaseError> error)
+void IDBDatabase::renameIndex(IDBIndex& index, const String& newName)
{
- ASSERT(m_transactions.contains(transactionId));
- m_transactions.get(transactionId)->onAbort(error);
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_versionChangeTransaction);
+ ASSERT(m_info.hasObjectStore(index.objectStore().info().name()));
+ ASSERT(m_info.infoForExistingObjectStore(index.objectStore().info().name())->hasIndex(index.info().name()));
+
+ m_info.infoForExistingObjectStore(index.objectStore().info().name())->infoForExistingIndex(index.info().identifier())->rename(newName);
+
+ m_versionChangeTransaction->renameIndex(index, newName);
}
-void IDBDatabase::onComplete(int64_t transactionId)
+ExceptionOr<Ref<IDBObjectStore>> IDBDatabase::createObjectStore(const String& name, ObjectStoreParameters&& parameters)
{
- ASSERT(m_transactions.contains(transactionId));
- m_transactions.get(transactionId)->onComplete();
+ LOG(IndexedDB, "IDBDatabase::createObjectStore - (%s %s)", m_info.name().utf8().data(), name.utf8().data());
+
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_versionChangeTransaction || m_versionChangeTransaction->isVersionChange());
+
+ if (!m_versionChangeTransaction)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': The database is not running a version change transaction.") };
+
+ if (!m_versionChangeTransaction->isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError };
+
+ if (m_info.hasObjectStore(name))
+ return Exception { IDBDatabaseException::ConstraintError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': An object store with the specified name already exists.") };
+
+ auto& keyPath = parameters.keyPath;
+ if (keyPath && !isIDBKeyPathValid(keyPath.value()))
+ return Exception { IDBDatabaseException::SyntaxError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': The keyPath option is not a valid key path.") };
+
+ if (keyPath && parameters.autoIncrement && ((WTF::holds_alternative<String>(keyPath.value()) && WTF::get<String>(keyPath.value()).isEmpty()) || WTF::holds_alternative<Vector<String>>(keyPath.value())))
+ return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'createObjectStore' on 'IDBDatabase': The autoIncrement option was set but the keyPath option was empty or an array.") };
+
+ // Install the new ObjectStore into the connection's metadata.
+ auto info = m_info.createNewObjectStore(name, WTFMove(keyPath), parameters.autoIncrement);
+
+ // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
+ return m_versionChangeTransaction->createObjectStore(info);
}
-PassRefPtr<DOMStringList> IDBDatabase::objectStoreNames() const
+ExceptionOr<Ref<IDBTransaction>> IDBDatabase::transaction(StringOrVectorOfStrings&& storeNames, IDBTransactionMode mode)
{
- RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
- for (IDBDatabaseMetadata::ObjectStoreMap::const_iterator it = m_metadata.objectStores.begin(); it != m_metadata.objectStores.end(); ++it)
- objectStoreNames->append(it->value.name);
- objectStoreNames->sort();
- return objectStoreNames.release();
+ LOG(IndexedDB, "IDBDatabase::transaction");
+
+ ASSERT(currentThread() == originThreadID());
+
+ if (m_closePending)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': The database connection is closing.") };
+
+ Vector<String> objectStores;
+ if (WTF::holds_alternative<Vector<String>>(storeNames))
+ objectStores = WTFMove(WTF::get<Vector<String>>(storeNames));
+ else
+ objectStores.append(WTFMove(WTF::get<String>(storeNames)));
+
+ if (objectStores.isEmpty())
+ return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': The storeNames parameter was empty.") };
+
+ if (mode != IDBTransactionMode::Readonly && mode != IDBTransactionMode::Readwrite)
+ return Exception { TypeError };
+
+ if (m_versionChangeTransaction && !m_versionChangeTransaction->isFinishedOrFinishing())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': A version change transaction is running.") };
+
+ // It is valid for javascript to pass in a list of object store names with the same name listed twice,
+ // so we need to put them all in a set to get a unique list.
+ HashSet<String> objectStoreSet;
+ for (auto& objectStore : objectStores)
+ objectStoreSet.add(objectStore);
+
+ objectStores.clear();
+ copyToVector(objectStoreSet, objectStores);
+
+ for (auto& objectStoreName : objectStores) {
+ if (m_info.hasObjectStore(objectStoreName))
+ continue;
+ return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'transaction' on 'IDBDatabase': One of the specified object stores was not found.") };
+ }
+
+ auto info = IDBTransactionInfo::clientTransaction(m_connectionProxy.get(), objectStores, mode);
+
+ LOG(IndexedDBOperations, "IDB creating transaction: %s", info.loggingString().utf8().data());
+ auto transaction = IDBTransaction::create(*this, info);
+
+ LOG(IndexedDB, "IDBDatabase::transaction - Added active transaction %s", info.identifier().loggingString().utf8().data());
+
+ m_activeTransactions.set(info.identifier(), transaction.ptr());
+
+ return WTFMove(transaction);
}
-uint64_t IDBDatabase::version() const
+ExceptionOr<void> IDBDatabase::deleteObjectStore(const String& objectStoreName)
{
- return m_metadata.version;
+ LOG(IndexedDB, "IDBDatabase::deleteObjectStore");
+
+ ASSERT(currentThread() == originThreadID());
+
+ if (!m_versionChangeTransaction)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'deleteObjectStore' on 'IDBDatabase': The database is not running a version change transaction.") };
+
+ if (!m_versionChangeTransaction->isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError };
+
+ if (!m_info.hasObjectStore(objectStoreName))
+ return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'deleteObjectStore' on 'IDBDatabase': The specified object store was not found.") };
+
+ m_info.deleteObjectStore(objectStoreName);
+ m_versionChangeTransaction->deleteObjectStore(objectStoreName);
+
+ return { };
}
-PassRefPtr<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, const Dictionary& options, ExceptionCode& ec)
+void IDBDatabase::close()
{
- IDBKeyPath keyPath;
- bool autoIncrement = false;
- if (!options.isUndefinedOrNull()) {
- String keyPathString;
- Vector<String> keyPathArray;
- if (options.get("keyPath", keyPathArray))
- keyPath = IDBKeyPath(keyPathArray);
- else if (options.getWithUndefinedOrNullCheck("keyPath", keyPathString))
- keyPath = IDBKeyPath(keyPathString);
-
- options.get("autoIncrement", autoIncrement);
+ LOG(IndexedDB, "IDBDatabase::close - %" PRIu64, m_databaseConnectionIdentifier);
+
+ ASSERT(currentThread() == originThreadID());
+
+ if (!m_closePending) {
+ m_closePending = true;
+ m_connectionProxy->databaseConnectionPendingClose(*this);
}
- return createObjectStore(name, keyPath, autoIncrement, ec);
+ maybeCloseInServer();
}
-PassRefPtr<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, const IDBKeyPath& keyPath, bool autoIncrement, ExceptionCode& ec)
+void IDBDatabase::didCloseFromServer(const IDBError& error)
{
- LOG(StorageAPI, "IDBDatabase::createObjectStore");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBCreateObjectStoreCall, IDBMethodsMax);
- if (!m_versionChangeTransaction) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_versionChangeTransaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
+ LOG(IndexedDB, "IDBDatabase::didCloseFromServer - %" PRIu64, m_databaseConnectionIdentifier);
- if (containsObjectStore(name)) {
- ec = IDBDatabaseException::ConstraintError;
- return 0;
- }
+ connectionToServerLost(error);
- if (!keyPath.isNull() && !keyPath.isValid()) {
- ec = IDBDatabaseException::SyntaxError;
- return 0;
- }
+ m_connectionProxy->confirmDidCloseFromServer(*this);
+}
- if (autoIncrement && ((keyPath.type() == IDBKeyPath::StringType && keyPath.string().isEmpty()) || keyPath.type() == IDBKeyPath::ArrayType)) {
- ec = IDBDatabaseException::InvalidAccessError;
- return 0;
- }
+void IDBDatabase::connectionToServerLost(const IDBError& error)
+{
+ LOG(IndexedDB, "IDBDatabase::connectionToServerLost - %" PRIu64, m_databaseConnectionIdentifier);
+
+ ASSERT(currentThread() == originThreadID());
+
+ m_closePending = true;
+ m_closedInServer = true;
+
+ for (auto& transaction : m_activeTransactions.values())
+ transaction->connectionClosedFromServer(error);
+
+ auto errorEvent = Event::create(m_eventNames.errorEvent, true, false);
+ errorEvent->setTarget(this);
- int64_t objectStoreId = m_metadata.maxObjectStoreId + 1;
- m_backend->createObjectStore(m_versionChangeTransaction->id(), objectStoreId, name, keyPath, autoIncrement);
+ if (auto* context = scriptExecutionContext())
+ context->eventQueue().enqueueEvent(WTFMove(errorEvent));
- IDBObjectStoreMetadata metadata(name, objectStoreId, keyPath, autoIncrement, IDBDatabaseBackend::MinimumIndexId);
- RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(metadata, m_versionChangeTransaction.get());
- m_metadata.objectStores.set(metadata.id, metadata);
- ++m_metadata.maxObjectStoreId;
+ auto closeEvent = Event::create(m_eventNames.closeEvent, true, false);
+ closeEvent->setTarget(this);
- m_versionChangeTransaction->objectStoreCreated(name, objectStore);
- return objectStore.release();
+ if (auto* context = scriptExecutionContext())
+ context->eventQueue().enqueueEvent(WTFMove(closeEvent));
}
-void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec)
+void IDBDatabase::maybeCloseInServer()
{
- LOG(StorageAPI, "IDBDatabase::deleteObjectStore");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBDeleteObjectStoreCall, IDBMethodsMax);
- if (!m_versionChangeTransaction) {
- ec = IDBDatabaseException::InvalidStateError;
- return;
- }
- if (!m_versionChangeTransaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
+ LOG(IndexedDB, "IDBDatabase::maybeCloseInServer - %" PRIu64, m_databaseConnectionIdentifier);
+
+ ASSERT(currentThread() == originThreadID());
+
+ if (m_closedInServer)
return;
- }
- int64_t objectStoreId = findObjectStoreId(name);
- if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
- ec = IDBDatabaseException::NotFoundError;
+ // 3.3.9 Database closing steps
+ // Wait for all transactions created using this connection to complete.
+ // Once they are complete, this connection is closed.
+ if (!m_activeTransactions.isEmpty() || !m_committingTransactions.isEmpty())
return;
- }
- m_backend->deleteObjectStore(m_versionChangeTransaction->id(), objectStoreId);
- m_versionChangeTransaction->objectStoreDeleted(name);
- m_metadata.objectStores.remove(objectStoreId);
+ m_closedInServer = true;
+ m_connectionProxy->databaseConnectionClosed(*this);
}
-PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, const Vector<String>& scope, const String& modeString, ExceptionCode& ec)
+const char* IDBDatabase::activeDOMObjectName() const
{
- LOG(StorageAPI, "IDBDatabase::transaction");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBTransactionCall, IDBMethodsMax);
- if (!scope.size()) {
- ec = IDBDatabaseException::InvalidAccessError;
- return 0;
- }
+ ASSERT(currentThread() == originThreadID());
+ return "IDBDatabase";
+}
- IndexedDB::TransactionMode mode = IDBTransaction::stringToMode(modeString, ec);
- if (ec)
- return 0;
+bool IDBDatabase::canSuspendForDocumentSuspension() const
+{
+ ASSERT(currentThread() == originThreadID());
- if (m_versionChangeTransaction || m_closePending) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
+ // FIXME: This value will sometimes be false when database operations are actually in progress.
+ // Such database operations do not yet exist.
+ return true;
+}
+
+void IDBDatabase::stop()
+{
+ LOG(IndexedDB, "IDBDatabase::stop - %" PRIu64, m_databaseConnectionIdentifier);
- Vector<int64_t> objectStoreIds;
- for (size_t i = 0; i < scope.size(); ++i) {
- int64_t objectStoreId = findObjectStoreId(scope[i]);
- if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
- ec = IDBDatabaseException::NotFoundError;
- return 0;
- }
- objectStoreIds.append(objectStoreId);
+ ASSERT(currentThread() == originThreadID());
+
+ removeAllEventListeners();
+
+ Vector<IDBResourceIdentifier> transactionIdentifiers;
+ transactionIdentifiers.reserveInitialCapacity(m_activeTransactions.size());
+
+ for (auto& id : m_activeTransactions.keys())
+ transactionIdentifiers.uncheckedAppend(id);
+
+ for (auto& id : transactionIdentifiers) {
+ IDBTransaction* transaction = m_activeTransactions.get(id);
+ if (transaction)
+ transaction->stop();
}
- int64_t transactionId = nextTransactionId();
- m_backend->createTransaction(transactionId, m_databaseCallbacks, objectStoreIds, mode);
+ close();
+}
+
+Ref<IDBTransaction> IDBDatabase::startVersionChangeTransaction(const IDBTransactionInfo& info, IDBOpenDBRequest& request)
+{
+ LOG(IndexedDB, "IDBDatabase::startVersionChangeTransaction %s", info.identifier().loggingString().utf8().data());
- RefPtr<IDBTransaction> transaction = IDBTransaction::create(context, transactionId, scope, mode, this);
- return transaction.release();
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_versionChangeTransaction);
+ ASSERT(info.mode() == IDBTransactionMode::Versionchange);
+ ASSERT(!m_closePending);
+ ASSERT(scriptExecutionContext());
+
+ Ref<IDBTransaction> transaction = IDBTransaction::create(*this, info, request);
+ m_versionChangeTransaction = &transaction.get();
+
+ m_activeTransactions.set(transaction->info().identifier(), &transaction.get());
+
+ return transaction;
}
-PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, const String& storeName, const String& mode, ExceptionCode& ec)
+void IDBDatabase::didStartTransaction(IDBTransaction& transaction)
{
- RefPtr<DOMStringList> storeNames = DOMStringList::create();
- storeNames->append(storeName);
- return transaction(context, storeNames, mode, ec);
+ LOG(IndexedDB, "IDBDatabase::didStartTransaction %s", transaction.info().identifier().loggingString().utf8().data());
+ ASSERT(!m_versionChangeTransaction);
+ ASSERT(currentThread() == originThreadID());
+
+ // It is possible for the client to have aborted a transaction before the server replies back that it has started.
+ if (m_abortingTransactions.contains(transaction.info().identifier()))
+ return;
+
+ m_activeTransactions.set(transaction.info().identifier(), &transaction);
}
-void IDBDatabase::forceClose()
+void IDBDatabase::willCommitTransaction(IDBTransaction& transaction)
{
- for (TransactionMap::const_iterator::Values it = m_transactions.begin().values(), end = m_transactions.end().values(); it != end; ++it)
- (*it)->abort(IGNORE_EXCEPTION);
- this->close();
+ LOG(IndexedDB, "IDBDatabase::willCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data());
+
+ ASSERT(currentThread() == originThreadID());
+
+ auto refTransaction = m_activeTransactions.take(transaction.info().identifier());
+ ASSERT(refTransaction);
+ m_committingTransactions.set(transaction.info().identifier(), WTFMove(refTransaction));
}
-void IDBDatabase::close()
+void IDBDatabase::didCommitTransaction(IDBTransaction& transaction)
{
- LOG(StorageAPI, "IDBDatabase::close");
- if (m_closePending)
- return;
+ LOG(IndexedDB, "IDBDatabase::didCommitTransaction %s", transaction.info().identifier().loggingString().utf8().data());
- m_closePending = true;
+ ASSERT(currentThread() == originThreadID());
+
+ if (m_versionChangeTransaction == &transaction)
+ m_info.setVersion(transaction.info().newVersion());
- if (m_transactions.isEmpty())
- closeConnection();
+ didCommitOrAbortTransaction(transaction);
}
-void IDBDatabase::closeConnection()
+void IDBDatabase::willAbortTransaction(IDBTransaction& transaction)
{
- ASSERT(m_closePending);
- ASSERT(m_transactions.isEmpty());
+ LOG(IndexedDB, "IDBDatabase::willAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
- // Closing may result in deallocating the last transaction, which could result in deleting
- // this IDBDatabase. We need the deallocation to happen after we are through.
- Ref<IDBDatabase> protect(*this);
+ ASSERT(currentThread() == originThreadID());
- m_backend->close(m_databaseCallbacks);
+ auto refTransaction = m_activeTransactions.take(transaction.info().identifier());
+ if (!refTransaction)
+ refTransaction = m_committingTransactions.take(transaction.info().identifier());
- if (m_contextStopped || !scriptExecutionContext())
- return;
+ ASSERT(refTransaction);
+ m_abortingTransactions.set(transaction.info().identifier(), WTFMove(refTransaction));
- EventQueue& eventQueue = scriptExecutionContext()->eventQueue();
- // Remove any pending versionchange events scheduled to fire on this
- // connection. They would have been scheduled by the backend when another
- // connection called setVersion, but the frontend connection is being
- // closed before they could fire.
- for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
- bool removed = eventQueue.cancelEvent(*m_enqueuedEvents[i]);
- ASSERT_UNUSED(removed, removed);
+ if (transaction.isVersionChange()) {
+ ASSERT(transaction.originalDatabaseInfo());
+ m_info = *transaction.originalDatabaseInfo();
+ m_closePending = true;
}
}
-void IDBDatabase::onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness)
+void IDBDatabase::didAbortTransaction(IDBTransaction& transaction)
{
- LOG(StorageAPI, "IDBDatabase::onVersionChange");
- if (m_contextStopped || !scriptExecutionContext())
- return;
+ LOG(IndexedDB, "IDBDatabase::didAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
- if (m_closePending)
- return;
+ ASSERT(currentThread() == originThreadID());
+
+ if (transaction.isVersionChange()) {
+ ASSERT(transaction.originalDatabaseInfo());
+ ASSERT(m_info.version() == transaction.originalDatabaseInfo()->version());
+ m_closePending = true;
+ maybeCloseInServer();
+ }
- enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, newVersionNullness));
+ didCommitOrAbortTransaction(transaction);
}
-void IDBDatabase::enqueueEvent(PassRefPtr<Event> event)
+void IDBDatabase::didCommitOrAbortTransaction(IDBTransaction& transaction)
{
- ASSERT(!m_contextStopped);
- ASSERT(scriptExecutionContext());
- event->setTarget(this);
- scriptExecutionContext()->eventQueue().enqueueEvent(event.get());
- m_enqueuedEvents.append(event);
+ LOG(IndexedDB, "IDBDatabase::didCommitOrAbortTransaction %s", transaction.info().identifier().loggingString().utf8().data());
+
+ ASSERT(currentThread() == originThreadID());
+
+ if (m_versionChangeTransaction == &transaction)
+ m_versionChangeTransaction = nullptr;
+
+#ifndef NDBEBUG
+ unsigned count = 0;
+ if (m_activeTransactions.contains(transaction.info().identifier()))
+ ++count;
+ if (m_committingTransactions.contains(transaction.info().identifier()))
+ ++count;
+ if (m_abortingTransactions.contains(transaction.info().identifier()))
+ ++count;
+
+ ASSERT(count == 1);
+#endif
+
+ m_activeTransactions.remove(transaction.info().identifier());
+ m_committingTransactions.remove(transaction.info().identifier());
+ m_abortingTransactions.remove(transaction.info().identifier());
+
+ if (m_closePending)
+ maybeCloseInServer();
}
-bool IDBDatabase::dispatchEvent(PassRefPtr<Event> event)
+void IDBDatabase::fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion)
{
- LOG(StorageAPI, "IDBDatabase::dispatchEvent");
- ASSERT(event->type() == eventNames().versionchangeEvent);
- for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
- if (m_enqueuedEvents[i].get() == event.get())
- m_enqueuedEvents.remove(i);
+ uint64_t currentVersion = m_info.version();
+ LOG(IndexedDB, "IDBDatabase::fireVersionChangeEvent - current version %" PRIu64 ", requested version %" PRIu64 ", connection %" PRIu64 " (%p)", currentVersion, requestedVersion, m_databaseConnectionIdentifier, this);
+
+ ASSERT(currentThread() == originThreadID());
+
+ if (!scriptExecutionContext() || m_closePending) {
+ connectionProxy().didFireVersionChangeEvent(m_databaseConnectionIdentifier, requestIdentifier);
+ return;
}
- return EventTarget::dispatchEvent(event.get());
+
+ Ref<Event> event = IDBVersionChangeEvent::create(requestIdentifier, currentVersion, requestedVersion, m_eventNames.versionchangeEvent);
+ event->setTarget(this);
+ scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
}
-int64_t IDBDatabase::findObjectStoreId(const String& name) const
+bool IDBDatabase::dispatchEvent(Event& event)
{
- for (IDBDatabaseMetadata::ObjectStoreMap::const_iterator it = m_metadata.objectStores.begin(); it != m_metadata.objectStores.end(); ++it) {
- if (it->value.name == name) {
- ASSERT(it->key != IDBObjectStoreMetadata::InvalidId);
- return it->key;
- }
- }
- return IDBObjectStoreMetadata::InvalidId;
+ LOG(IndexedDB, "IDBDatabase::dispatchEvent (%" PRIu64 ") (%p)", m_databaseConnectionIdentifier, this);
+ ASSERT(currentThread() == originThreadID());
+
+ bool result = EventTargetWithInlineData::dispatchEvent(event);
+
+ if (event.isVersionChangeEvent() && event.type() == m_eventNames.versionchangeEvent)
+ connectionProxy().didFireVersionChangeEvent(m_databaseConnectionIdentifier, downcast<IDBVersionChangeEvent>(event).requestIdentifier());
+
+ return result;
}
-bool IDBDatabase::hasPendingActivity() const
+void IDBDatabase::didCreateIndexInfo(const IDBIndexInfo& info)
{
- // The script wrapper must not be collected before the object is closed or
- // we can't fire a "versionchange" event to let script manually close the connection.
- return !m_closePending && hasEventListeners() && !m_contextStopped;
+ ASSERT(currentThread() == originThreadID());
+
+ auto* objectStore = m_info.infoForExistingObjectStore(info.objectStoreIdentifier());
+ ASSERT(objectStore);
+ objectStore->addExistingIndex(info);
}
-void IDBDatabase::stop()
+void IDBDatabase::didDeleteIndexInfo(const IDBIndexInfo& info)
{
- // Stop fires at a deterministic time, so we need to call close in it.
- close();
+ ASSERT(currentThread() == originThreadID());
- m_contextStopped = true;
+ auto* objectStore = m_info.infoForExistingObjectStore(info.objectStoreIdentifier());
+ ASSERT(objectStore);
+ objectStore->deleteIndex(info.name());
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabase.h b/Source/WebCore/Modules/indexeddb/IDBDatabase.h
index 5cf6032c5..56b94c6c0 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabase.h
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabase.h
@@ -1,143 +1,138 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBDatabase_h
-#define IDBDatabase_h
-
-#include "ActiveDOMObject.h"
-#include "DOMStringList.h"
-#include "Dictionary.h"
-#include "Event.h"
-#include "EventTarget.h"
-#include "IDBDatabaseCallbacks.h"
-#include "IDBDatabaseMetadata.h"
-#include "IDBObjectStore.h"
-#include "IDBTransaction.h"
-#include "IndexedDB.h"
-#include "ScriptWrappable.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "EventTarget.h"
+#include "IDBActiveDOMObject.h"
+#include "IDBConnectionProxy.h"
+#include "IDBConnectionToServer.h"
+#include "IDBDatabaseInfo.h"
+#include "IDBKeyPath.h"
+#include "IDBTransactionMode.h"
+
namespace WebCore {
-class ScriptExecutionContext;
+class DOMStringList;
+class IDBObjectStore;
+class IDBOpenDBRequest;
+class IDBResultData;
+class IDBTransaction;
+class IDBTransactionInfo;
-typedef int ExceptionCode;
+struct EventNames;
-class IDBDatabase final : public RefCounted<IDBDatabase>, public ScriptWrappable, public EventTargetWithInlineData, public ActiveDOMObject {
+class IDBDatabase : public ThreadSafeRefCounted<IDBDatabase>, public EventTargetWithInlineData, public IDBActiveDOMObject {
public:
- static PassRefPtr<IDBDatabase> create(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackend>, PassRefPtr<IDBDatabaseCallbacks>);
- ~IDBDatabase();
+ static Ref<IDBDatabase> create(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBResultData&);
- void setMetadata(const IDBDatabaseMetadata& metadata) { m_metadata = metadata; }
- void transactionCreated(IDBTransaction*);
- void transactionFinished(IDBTransaction*);
+ virtual ~IDBDatabase();
- // Implement the IDL
- const String name() const { return m_metadata.name; }
+ // IDBDatabase IDL
+ const String name() const;
uint64_t version() const;
- PassRefPtr<DOMStringList> objectStoreNames() const;
-
- PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const Dictionary&, ExceptionCode&);
- PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const IDBKeyPath&, bool autoIncrement, ExceptionCode&);
- PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext* context, PassRefPtr<DOMStringList> scope, const String& mode, ExceptionCode& ec) { return transaction(context, *scope, mode, ec); }
- PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext*, const Vector<String>&, const String& mode, ExceptionCode&);
- PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext*, const String&, const String& mode, ExceptionCode&);
- void deleteObjectStore(const String& name, ExceptionCode&);
- void close();
+ RefPtr<DOMStringList> objectStoreNames() const;
+
+ struct ObjectStoreParameters {
+ std::optional<IDBKeyPath> keyPath;
+ bool autoIncrement;
+ };
- DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(versionchange);
+ ExceptionOr<Ref<IDBObjectStore>> createObjectStore(const String& name, ObjectStoreParameters&&);
- // IDBDatabaseCallbacks
- virtual void onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness);
- virtual void onAbort(int64_t, PassRefPtr<IDBDatabaseError>);
- virtual void onComplete(int64_t);
+ using StringOrVectorOfStrings = WTF::Variant<String, Vector<String>>;
+ ExceptionOr<Ref<IDBTransaction>> transaction(StringOrVectorOfStrings&& storeNames, IDBTransactionMode);
+ ExceptionOr<void> deleteObjectStore(const String& name);
+ void close();
- // ActiveDOMObject
- virtual bool hasPendingActivity() const override;
+ void renameObjectStore(IDBObjectStore&, const String& newName);
+ void renameIndex(IDBIndex&, const String& newName);
// EventTarget
- virtual EventTargetInterface eventTargetInterface() const override final { return IDBDatabaseEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override final { return ActiveDOMObject::scriptExecutionContext(); }
+ EventTargetInterface eventTargetInterface() const final { return IDBDatabaseEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
+ void refEventTarget() final { ThreadSafeRefCounted<IDBDatabase>::ref(); }
+ void derefEventTarget() final { ThreadSafeRefCounted<IDBDatabase>::deref(); }
- bool isClosePending() const { return m_closePending; }
- void forceClose();
- const IDBDatabaseMetadata metadata() const { return m_metadata; }
- void enqueueEvent(PassRefPtr<Event>);
+ using ThreadSafeRefCounted<IDBDatabase>::ref;
+ using ThreadSafeRefCounted<IDBDatabase>::deref;
- using EventTarget::dispatchEvent;
- virtual bool dispatchEvent(PassRefPtr<Event>) override;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
+ void stop() final;
- int64_t findObjectStoreId(const String& name) const;
- bool containsObjectStore(const String& name) const
- {
- return findObjectStoreId(name) != IDBObjectStoreMetadata::InvalidId;
- }
+ const IDBDatabaseInfo& info() const { return m_info; }
+ uint64_t databaseConnectionIdentifier() const { return m_databaseConnectionIdentifier; }
- IDBDatabaseBackend* backend() const { return m_backend.get(); }
+ Ref<IDBTransaction> startVersionChangeTransaction(const IDBTransactionInfo&, IDBOpenDBRequest&);
+ void didStartTransaction(IDBTransaction&);
- static int64_t nextTransactionId();
+ void willCommitTransaction(IDBTransaction&);
+ void didCommitTransaction(IDBTransaction&);
+ void willAbortTransaction(IDBTransaction&);
+ void didAbortTransaction(IDBTransaction&);
- using RefCounted<IDBDatabase>::ref;
- using RefCounted<IDBDatabase>::deref;
+ void fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion);
+ void didCloseFromServer(const IDBError&);
+ void connectionToServerLost(const IDBError&);
-private:
- IDBDatabase(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackend>, PassRefPtr<IDBDatabaseCallbacks>);
+ IDBClient::IDBConnectionProxy& connectionProxy() { return m_connectionProxy.get(); }
- // ActiveDOMObject
- virtual void stop() override;
+ void didCreateIndexInfo(const IDBIndexInfo&);
+ void didDeleteIndexInfo(const IDBIndexInfo&);
- // EventTarget
- virtual void refEventTarget() override final { ref(); }
- virtual void derefEventTarget() override final { deref(); }
+ bool isClosingOrClosed() const { return m_closePending || m_closedInServer; }
- void closeConnection();
+ bool dispatchEvent(Event&) final;
- IDBDatabaseMetadata m_metadata;
- RefPtr<IDBDatabaseBackend> m_backend;
- RefPtr<IDBTransaction> m_versionChangeTransaction;
- typedef HashMap<int64_t, IDBTransaction*> TransactionMap;
- TransactionMap m_transactions;
+ bool hasPendingActivity() const final;
+
+private:
+ IDBDatabase(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBResultData&);
- bool m_closePending;
- bool m_contextStopped;
+ void didCommitOrAbortTransaction(IDBTransaction&);
- // Keep track of the versionchange events waiting to be fired on this
- // database so that we can cancel them if the database closes.
- Vector<RefPtr<Event>> m_enqueuedEvents;
+ void maybeCloseInServer();
- RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
+ Ref<IDBClient::IDBConnectionProxy> m_connectionProxy;
+ IDBDatabaseInfo m_info;
+ uint64_t m_databaseConnectionIdentifier { 0 };
+
+ bool m_closePending { false };
+ bool m_closedInServer { false };
+
+ RefPtr<IDBTransaction> m_versionChangeTransaction;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_activeTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_committingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_abortingTransactions;
+
+ const EventNames& m_eventNames; // Need to cache this so we can use it from GC threads.
};
} // namespace WebCore
-#endif
-
-#endif // IDBDatabase_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabase.idl b/Source/WebCore/Modules/indexeddb/IDBDatabase.idl
index 0b5e57e4e..3241c636b 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabase.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabase.idl
@@ -27,24 +27,29 @@
[
Conditional=INDEXED_DATABASE,
ActiveDOMObject,
- EventTarget,
- JSNoStaticTables,
- JSGenerateToJSObject,
- JSGenerateToNativeObject,
+ SkipVTableValidation,
] interface IDBDatabase : EventTarget {
readonly attribute DOMString name;
readonly attribute unsigned long long version;
readonly attribute DOMStringList objectStoreNames;
- [Custom, RaisesException] IDBObjectStore createObjectStore(DOMString name, optional Dictionary options);
- [RaisesException] void deleteObjectStore(DOMString name);
- [CallWith=ScriptExecutionContext, RaisesException] IDBTransaction transaction(DOMString storeName, [Default=NullString] optional DOMString mode);
- [CallWith=ScriptExecutionContext, RaisesException] IDBTransaction transaction(sequence<DOMString> storeNames, [Default=NullString] optional DOMString mode);
- void close();
+ [MayThrowException] IDBObjectStore createObjectStore(DOMString name, optional IDBObjectStoreParameters parameters);
+ [MayThrowException] void deleteObjectStore(DOMString name);
+
+ [MayThrowException] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames, optional IDBTransactionMode mode = "readonly");
- attribute EventListener onabort;
- attribute EventListener onerror;
- attribute EventListener onversionchange;
+ // FIXME: This is not part of the spec, but is needed for compatibility.
+ // See https://github.com/w3c/IndexedDB/issues/85
+ [MayThrowException] IDBTransaction transaction(DOMStringList storeNames, optional IDBTransactionMode mode = "readonly");
+ void close();
+ attribute EventHandler onabort;
+ attribute EventHandler onclose;
+ attribute EventHandler onerror;
+ attribute EventHandler onversionchange;
};
+dictionary IDBObjectStoreParameters {
+ (DOMString or sequence<DOMString>)? keyPath = null;
+ boolean autoIncrement = false;
+};
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp
deleted file mode 100644
index 3cef3f3cd..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 "IDBDatabaseBackend.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBCursorBackend.h"
-#include "IDBDatabaseCallbacks.h"
-#include "IDBDatabaseException.h"
-#include "IDBFactoryBackendInterface.h"
-#include "IDBKeyRange.h"
-#include "IDBRecordIdentifier.h"
-#include "IDBServerConnection.h"
-#include "IDBTransactionBackend.h"
-#include "IDBTransactionCoordinator.h"
-#include "Logging.h"
-#include "SharedBuffer.h"
-#include <wtf/TemporaryChange.h>
-
-namespace WebCore {
-
-PassRefPtr<IDBDatabaseBackend> IDBDatabaseBackend::create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection)
-{
- RefPtr<IDBDatabaseBackend> backend = adoptRef(new IDBDatabaseBackend(name, uniqueIdentifier, factory, serverConnection));
- backend->openInternalAsync();
-
- return backend.release();
-}
-
-IDBDatabaseBackend::IDBDatabaseBackend(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection)
- : m_metadata(name, InvalidId, 0, InvalidId)
- , m_identifier(uniqueIdentifier)
- , m_factory(factory)
- , m_serverConnection(serverConnection)
- , m_transactionCoordinator(IDBTransactionCoordinator::create())
- , m_closingConnection(false)
-{
- ASSERT(!m_metadata.name.isNull());
-}
-
-void IDBDatabaseBackend::addObjectStore(const IDBObjectStoreMetadata& objectStore, int64_t newMaxObjectStoreId)
-{
- ASSERT(!m_metadata.objectStores.contains(objectStore.id));
- if (newMaxObjectStoreId != IDBObjectStoreMetadata::InvalidId) {
- ASSERT(m_metadata.maxObjectStoreId < newMaxObjectStoreId);
- m_metadata.maxObjectStoreId = newMaxObjectStoreId;
- }
- m_metadata.objectStores.set(objectStore.id, objectStore);
-}
-
-void IDBDatabaseBackend::removeObjectStore(int64_t objectStoreId)
-{
- ASSERT(m_metadata.objectStores.contains(objectStoreId));
- m_metadata.objectStores.remove(objectStoreId);
-}
-
-void IDBDatabaseBackend::addIndex(int64_t objectStoreId, const IDBIndexMetadata& index, int64_t newMaxIndexId)
-{
- ASSERT(m_metadata.objectStores.contains(objectStoreId));
- IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
-
- ASSERT(!objectStore.indexes.contains(index.id));
- objectStore.indexes.set(index.id, index);
- if (newMaxIndexId != IDBIndexMetadata::InvalidId) {
- ASSERT(objectStore.maxIndexId < newMaxIndexId);
- objectStore.maxIndexId = newMaxIndexId;
- }
- m_metadata.objectStores.set(objectStoreId, objectStore);
-}
-
-void IDBDatabaseBackend::removeIndex(int64_t objectStoreId, int64_t indexId)
-{
- ASSERT(m_metadata.objectStores.contains(objectStoreId));
- IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
-
- ASSERT(objectStore.indexes.contains(indexId));
- objectStore.indexes.remove(indexId);
- m_metadata.objectStores.set(objectStoreId, objectStore);
-}
-
-void IDBDatabaseBackend::openInternalAsync()
-{
- RefPtr<IDBDatabaseBackend> self = this;
- m_serverConnection->getOrEstablishIDBDatabaseMetadata([self](const IDBDatabaseMetadata& metadata, bool success) {
- self->didOpenInternalAsync(metadata, success);
- });
-}
-
-void IDBDatabaseBackend::didOpenInternalAsync(const IDBDatabaseMetadata& metadata, bool success)
-{
- if (!success) {
- processPendingOpenCalls(false);
- return;
- }
-
- m_metadata = metadata;
-
- processPendingCalls();
-}
-
-IDBDatabaseBackend::~IDBDatabaseBackend()
-{
- m_factory->removeIDBDatabaseBackend(m_identifier);
-}
-
-void IDBDatabaseBackend::createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::createObjectStore");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
- ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange);
-
- ASSERT(!m_metadata.objectStores.contains(objectStoreId));
- IDBObjectStoreMetadata objectStoreMetadata(name, objectStoreId, keyPath, autoIncrement, IDBDatabaseBackend::MinimumIndexId);
-
- transaction->scheduleCreateObjectStoreOperation(objectStoreMetadata);
- addObjectStore(objectStoreMetadata, objectStoreId);
-}
-
-void IDBDatabaseBackend::deleteObjectStore(int64_t transactionId, int64_t objectStoreId)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::deleteObjectStore");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
- ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange);
-
- ASSERT(m_metadata.objectStores.contains(objectStoreId));
- const IDBObjectStoreMetadata& objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
-
- transaction->scheduleDeleteObjectStoreOperation(objectStoreMetadata);
- removeObjectStore(objectStoreId);
-}
-
-void IDBDatabaseBackend::createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::createIndex");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
- ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange);
-
- ASSERT(m_metadata.objectStores.contains(objectStoreId));
- const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
-
- ASSERT(!objectStore.indexes.contains(indexId));
- const IDBIndexMetadata indexMetadata(name, indexId, keyPath, unique, multiEntry);
-
- transaction->scheduleCreateIndexOperation(objectStoreId, indexMetadata);
-
- addIndex(objectStoreId, indexMetadata, indexId);
-}
-
-void IDBDatabaseBackend::deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::deleteIndex");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
- ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange);
-
- ASSERT(m_metadata.objectStores.contains(objectStoreId));
- const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
-
- ASSERT(objectStore.indexes.contains(indexId));
- const IDBIndexMetadata& indexMetadata = objectStore.indexes.get(indexId);
-
- transaction->scheduleDeleteIndexOperation(objectStoreId, indexMetadata);
-
- removeIndex(objectStoreId, indexId);
-}
-
-void IDBDatabaseBackend::commit(int64_t transactionId)
-{
- // The frontend suggests that we commit, but we may have previously initiated an abort, and so have disposed of the transaction. onAbort has already been dispatched to the frontend, so it will find out about that asynchronously.
- if (m_transactions.contains(transactionId))
- m_transactions.get(transactionId)->commit();
-}
-
-void IDBDatabaseBackend::abort(int64_t transactionId)
-{
- // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it.
- if (m_transactions.contains(transactionId))
- m_transactions.get(transactionId)->abort();
-}
-
-void IDBDatabaseBackend::abort(int64_t transactionId, PassRefPtr<IDBDatabaseError> error)
-{
- // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it.
- if (m_transactions.contains(transactionId))
- m_transactions.get(transactionId)->abort(error);
-}
-
-void IDBDatabaseBackend::get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, bool keyOnly, PassRefPtr<IDBCallbacks> callbacks)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::get");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
-
- transaction->scheduleGetOperation(m_metadata, objectStoreId, indexId, keyRange, keyOnly ? IndexedDB::CursorType::KeyOnly : IndexedDB::CursorType::KeyAndValue, callbacks);
-}
-
-void IDBDatabaseBackend::put(int64_t transactionId, int64_t objectStoreId, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::put");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
- ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
-
- const IDBObjectStoreMetadata objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
-
- ASSERT(objectStoreMetadata.autoIncrement || key.get());
-
- transaction->schedulePutOperation(objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys);
-}
-
-void IDBDatabaseBackend::setIndexKeys(int64_t transactionID, int64_t objectStoreID, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIDs, const Vector<IndexKeys>& indexKeys)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::setIndexKeys");
- ASSERT(prpPrimaryKey);
- ASSERT(m_metadata.objectStores.contains(objectStoreID));
-
- RefPtr<IDBTransactionBackend> transaction = m_transactions.get(transactionID);
- if (!transaction)
- return;
- ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange);
-
- RefPtr<IDBKey> primaryKey = prpPrimaryKey;
- m_serverConnection->setIndexKeys(transactionID, m_metadata.id, objectStoreID, m_metadata.objectStores.get(objectStoreID), *primaryKey, indexIDs, indexKeys, [transaction](PassRefPtr<IDBDatabaseError> error) {
- if (error)
- transaction->abort(error);
- });
-}
-
-void IDBDatabaseBackend::setIndexesReady(int64_t transactionId, int64_t, const Vector<int64_t>& indexIds)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::setIndexesReady");
-
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
-
- transaction->scheduleSetIndexesReadyOperation(indexIds.size());
-}
-
-void IDBDatabaseBackend::openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, bool keyOnly, TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::openCursor");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
-
- transaction->scheduleOpenCursorOperation(objectStoreId, indexId, keyRange, direction, keyOnly ? IndexedDB::CursorType::KeyOnly : IndexedDB::CursorType::KeyAndValue, taskType, callbacks);
-}
-
-void IDBDatabaseBackend::count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::count");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
-
- ASSERT(m_metadata.objectStores.contains(objectStoreId));
- transaction->scheduleCountOperation(objectStoreId, indexId, keyRange, callbacks);
-}
-
-
-void IDBDatabaseBackend::deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::deleteRange");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
- ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
-
- transaction->scheduleDeleteRangeOperation(objectStoreId, keyRange, callbacks);
-}
-
-void IDBDatabaseBackend::clearObjectStore(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::clear");
- IDBTransactionBackend* transaction = m_transactions.get(transactionId);
- if (!transaction)
- return;
- ASSERT(transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
-
- transaction->scheduleClearObjectStoreOperation(objectStoreId, callbacks);
-}
-
-void IDBDatabaseBackend::transactionStarted(IDBTransactionBackend* transaction)
-{
- if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) {
- ASSERT(!m_runningVersionChangeTransaction);
- m_runningVersionChangeTransaction = transaction;
- }
-}
-
-void IDBDatabaseBackend::transactionFinished(IDBTransactionBackend* rawTransaction)
-{
- RefPtr<IDBTransactionBackend> transaction = rawTransaction;
- ASSERT(m_transactions.contains(transaction->id()));
- ASSERT(m_transactions.get(transaction->id()) == transaction.get());
- m_transactions.remove(transaction->id());
- if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) {
- ASSERT(transaction.get() == m_runningVersionChangeTransaction.get());
- m_runningVersionChangeTransaction.clear();
- }
-}
-
-void IDBDatabaseBackend::transactionFinishedAndAbortFired(IDBTransactionBackend* rawTransaction)
-{
- RefPtr<IDBTransactionBackend> transaction = rawTransaction;
- if (transaction->mode() == IndexedDB::TransactionMode::VersionChange) {
- // If this was an open-with-version call, there will be a "second
- // half" open call waiting for us in processPendingCalls.
- // FIXME: When we no longer support setVersion, assert such a thing.
- if (m_pendingSecondHalfOpen) {
- m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "Version change transaction was aborted in upgradeneeded event handler."));
- m_pendingSecondHalfOpen.release();
- }
- processPendingCalls();
- }
-}
-
-void IDBDatabaseBackend::transactionFinishedAndCompleteFired(IDBTransactionBackend* rawTransaction)
-{
- RefPtr<IDBTransactionBackend> transaction = rawTransaction;
- if (transaction->mode() == IndexedDB::TransactionMode::VersionChange)
- processPendingCalls();
-}
-
-size_t IDBDatabaseBackend::connectionCount()
-{
- // This does not include pending open calls, as those should not block version changes and deletes.
- return m_databaseCallbacksSet.size();
-}
-
-void IDBDatabaseBackend::processPendingCalls()
-{
- if (m_pendingSecondHalfOpen) {
- ASSERT(m_pendingSecondHalfOpen->version() == m_metadata.version);
- ASSERT(m_metadata.id != InvalidId);
- m_pendingSecondHalfOpen->callbacks()->onSuccess(this, this->metadata());
- m_pendingSecondHalfOpen.release();
- // Fall through when complete, as pending deletes may be (partially) unblocked.
- }
-
- // Note that this check is only an optimization to reduce queue-churn and
- // not necessary for correctness; deleteDatabase and openConnection will
- // requeue their calls if this condition is true.
- if (m_runningVersionChangeTransaction)
- return;
-
- if (!m_pendingDeleteCalls.isEmpty() && isDeleteDatabaseBlocked())
- return;
- while (!m_pendingDeleteCalls.isEmpty()) {
- OwnPtr<IDBPendingDeleteCall> pendingDeleteCall = m_pendingDeleteCalls.takeFirst();
- m_deleteCallbacksWaitingCompletion.add(pendingDeleteCall->callbacks());
- deleteDatabaseAsync(pendingDeleteCall->callbacks());
- }
-
- // deleteDatabaseAsync should never re-queue calls.
- ASSERT(m_pendingDeleteCalls.isEmpty());
-
- // If there are any database deletions waiting for completion, we're done for now.
- // Further callbacks will be handled in a future call to processPendingCalls().
- if (!m_deleteCallbacksWaitingCompletion.isEmpty())
- return;
-
- if (m_runningVersionChangeTransaction)
- return;
-
- processPendingOpenCalls(true);
-}
-
-void IDBDatabaseBackend::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();
- 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;
- 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));
- }
- }
-}
-
-void IDBDatabaseBackend::createTransaction(int64_t transactionID, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIDs, IndexedDB::TransactionMode mode)
-{
- RefPtr<IDBTransactionBackend> transaction = IDBTransactionBackend::create(this, transactionID, callbacks, objectStoreIDs, mode);
-
- ASSERT(!m_transactions.contains(transactionID));
- m_transactions.add(transactionID, transaction.get());
-}
-
-void IDBDatabaseBackend::openConnection(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
-{
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
-
- m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, version));
-
- processPendingCalls();
-}
-
-void IDBDatabaseBackend::openConnectionInternal(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
-{
- 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;
-
- if (version == IDBDatabaseMetadata::DefaultIntVersion) {
- // FIXME: this comments was related to Chromium code. It may be incorrect
- // For unit tests only - skip upgrade steps. Calling from script with DefaultIntVersion throws exception.
- ASSERT(isNewDatabase);
- m_databaseCallbacksSet.add(databaseCallbacks);
- callbacks->onSuccess(this, this->metadata());
- return;
- }
-
- if (version == IDBDatabaseMetadata::NoIntVersion) {
- if (!isNewDatabase) {
- m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(databaseCallbacks));
- callbacks->onSuccess(this, this->metadata());
- return;
- }
- // Spec says: If no version is specified and no database exists, set database version to 1.
- version = 1;
- }
-
- if (version > m_metadata.version) {
- runIntVersionChangeTransaction(callbacks, databaseCallbacks, transactionId, version);
- return;
- }
-
- if (version < m_metadata.version) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::VersionError, String::format("The requested version (%llu) is less than the existing version (%llu).", static_cast<unsigned long long>(version), static_cast<unsigned long long>(m_metadata.version))));
- return;
- }
-
- ASSERT(version == m_metadata.version);
- m_databaseCallbacksSet.add(databaseCallbacks);
- callbacks->onSuccess(this, this->metadata());
-}
-
-void IDBDatabaseBackend::runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, int64_t requestedVersion)
-{
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
- ASSERT(callbacks);
- for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
- // Front end ensures the event is not fired at connections that have closePending set.
- if (*it != databaseCallbacks)
- (*it)->onVersionChange(m_metadata.version, requestedVersion, IndexedDB::VersionNullness::Null);
- }
- // The spec dictates we wait until all the version change events are
- // delivered and then check m_databaseCallbacks.empty() before proceeding
- // or firing a blocked event, but instead we should be consistent with how
- // the old setVersion (incorrectly) did it.
- // FIXME: Remove the call to onBlocked and instead wait until the frontend
- // tells us that all the blocked events have been delivered. See
- // https://bugs.webkit.org/show_bug.cgi?id=71130
- if (connectionCount())
- callbacks->onBlocked(m_metadata.version);
- // FIXME: Add test for m_runningVersionChangeTransaction.
- if (m_runningVersionChangeTransaction || connectionCount()) {
- m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, requestedVersion));
- return;
- }
-
- Vector<int64_t> objectStoreIds;
- createTransaction(transactionId, databaseCallbacks, objectStoreIds, IndexedDB::TransactionMode::VersionChange);
- RefPtr<IDBTransactionBackend> transaction = m_transactions.get(transactionId);
-
- transaction->scheduleVersionChangeOperation(requestedVersion, callbacks, databaseCallbacks, m_metadata);
-
- ASSERT(!m_pendingSecondHalfOpen);
- m_databaseCallbacksSet.add(databaseCallbacks);
-}
-
-void IDBDatabaseBackend::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
-{
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- if (isDeleteDatabaseBlocked()) {
- for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
- // Front end ensures the event is not fired at connections that have closePending set.
- (*it)->onVersionChange(m_metadata.version, 0, IndexedDB::VersionNullness::Null);
- }
- // FIXME: Only fire onBlocked if there are open connections after the
- // VersionChangeEvents are received, not just set up to fire.
- // https://bugs.webkit.org/show_bug.cgi?id=71130
- callbacks->onBlocked(m_metadata.version);
- m_pendingDeleteCalls.append(IDBPendingDeleteCall::create(callbacks.release()));
- return;
- }
- deleteDatabaseAsync(callbacks.release());
-}
-
-bool IDBDatabaseBackend::isDeleteDatabaseBlocked()
-{
- return connectionCount();
-}
-
-void IDBDatabaseBackend::deleteDatabaseAsync(PassRefPtr<IDBCallbacks> callbacks)
-{
- ASSERT(!isDeleteDatabaseBlocked());
-
- RefPtr<IDBDatabaseBackend> self(this);
- m_serverConnection->deleteDatabase(m_metadata.name, [self, callbacks](bool success) {
- ASSERT(self->m_deleteCallbacksWaitingCompletion.contains(callbacks));
- self->m_deleteCallbacksWaitingCompletion.remove(callbacks);
-
- // If this IDBDatabaseBackend was closed while waiting for deleteDatabase to complete, no point in performing any callbacks.
- if (!self->m_serverConnection->isClosed())
- return;
-
- if (success) {
- self->m_metadata.id = InvalidId;
- self->m_metadata.version = IDBDatabaseMetadata::NoIntVersion;
- self->m_metadata.objectStores.clear();
- callbacks->onSuccess();
- } else
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error deleting database."));
-
- self->processPendingCalls();
- });
-}
-
-void IDBDatabaseBackend::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
-{
- RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
- ASSERT(m_databaseCallbacksSet.contains(callbacks));
-
- m_databaseCallbacksSet.remove(callbacks);
- if (m_pendingSecondHalfOpen && m_pendingSecondHalfOpen->databaseCallbacks() == callbacks) {
- m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed."));
- m_pendingSecondHalfOpen.release();
- }
-
- if (connectionCount() > 1)
- return;
-
- // processPendingCalls allows the inspector to process a pending open call
- // and call close, reentering IDBDatabaseBackend::close. Then the
- // backend would be removed both by the inspector closing its connection, and
- // by the connection that first called close.
- // To avoid that situation, don't proceed in case of reentrancy.
- if (m_closingConnection)
- return;
- TemporaryChange<bool> closingConnection(m_closingConnection, true);
- processPendingCalls();
-
- // FIXME: Add a test for the m_pendingOpenCalls cases below.
- if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingDeleteCalls.size()) {
- TransactionMap transactions(m_transactions);
- RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Connection is closing.");
- for (TransactionMap::const_iterator::Values it = transactions.values().begin(), end = transactions.values().end(); it != end; ++it)
- (*it)->abort(error);
-
- ASSERT(m_transactions.isEmpty());
-
- m_serverConnection->close();
-
- // This check should only be false in unit tests.
- ASSERT(m_factory);
- if (m_factory)
- m_factory->removeIDBDatabaseBackend(m_identifier);
- }
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h
deleted file mode 100644
index 6815d6cc3..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h
+++ /dev/null
@@ -1,174 +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.
- *
- * 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 IDBDatabaseBackend_h
-#define IDBDatabaseBackend_h
-
-#include "IDBDatabaseCallbacks.h"
-#include "IDBDatabaseMetadata.h"
-#include "IDBKeyRange.h"
-#include "IDBPendingDeleteCall.h"
-#include "IDBPendingOpenCall.h"
-
-#include <stdint.h>
-#include <wtf/Deque.h>
-#include <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-class IDBDatabase;
-class IDBFactoryBackendInterface;
-class IDBKey;
-class IDBKeyPath;
-class IDBServerConnection;
-class IDBTransactionBackend;
-class IDBTransactionCoordinator;
-class SharedBuffer;
-
-struct IDBDatabaseMetadata;
-struct IDBIndexMetadata;
-struct IDBObjectStoreMetadata;
-
-typedef Vector<RefPtr<IDBKey>> IndexKeys;
-typedef int ExceptionCode;
-
-class IDBDatabaseBackend : public RefCounted<IDBDatabaseBackend> {
-public:
- static PassRefPtr<IDBDatabaseBackend> create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface*, IDBServerConnection&);
- ~IDBDatabaseBackend();
-
- IDBServerConnection& serverConnection() { return m_serverConnection.get(); }
-
- static const int64_t InvalidId = 0;
- int64_t id() const { return m_metadata.id; }
- void addObjectStore(const IDBObjectStoreMetadata&, int64_t newMaxObjectStoreId);
- void removeObjectStore(int64_t objectStoreId);
- void addIndex(int64_t objectStoreId, const IDBIndexMetadata&, int64_t newMaxIndexId);
- void removeIndex(int64_t objectStoreId, int64_t indexId);
-
- void openConnection(PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version);
- void deleteDatabase(PassRefPtr<IDBCallbacks>);
-
- // IDBDatabaseBackend
- void createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath&, bool autoIncrement);
- void deleteObjectStore(int64_t transactionId, int64_t objectStoreId);
- void createTransaction(int64_t transactionId, PassRefPtr<IDBDatabaseCallbacks>, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode);
- void close(PassRefPtr<IDBDatabaseCallbacks>);
-
- void commit(int64_t transactionId);
- void abort(int64_t transactionId);
- void abort(int64_t transactionId, PassRefPtr<IDBDatabaseError>);
-
- void createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath&, bool unique, bool multiEntry);
- void deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId);
-
- IDBTransactionCoordinator* transactionCoordinator() const { return m_transactionCoordinator.get(); }
- void transactionStarted(IDBTransactionBackend*);
- void transactionFinished(IDBTransactionBackend*);
- void transactionFinishedAndCompleteFired(IDBTransactionBackend*);
- void transactionFinishedAndAbortFired(IDBTransactionBackend*);
-
- enum TaskType {
- NormalTask = 0,
- PreemptiveTask
- };
-
- enum PutMode {
- AddOrUpdate,
- AddOnly,
- CursorUpdate
- };
-
- static const int64_t MinimumIndexId = 30;
-
- void get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, bool keyOnly, PassRefPtr<IDBCallbacks>);
- void put(int64_t transactionId, int64_t objectStoreId, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey>, PutMode, PassRefPtr<IDBCallbacks>, const Vector<int64_t>& indexIds, const Vector<IndexKeys>&);
- void setIndexKeys(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIds, const Vector<IndexKeys>&);
- void setIndexesReady(int64_t transactionId, int64_t objectStoreId, const Vector<int64_t>& indexIds);
- void openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, IndexedDB::CursorDirection, bool keyOnly, TaskType, PassRefPtr<IDBCallbacks>);
- void count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void clearObjectStore(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBCallbacks>);
-
- const IDBDatabaseMetadata& metadata() const { return m_metadata; }
- void setCurrentVersion(uint64_t version) { m_metadata.version = version; }
-
- bool hasPendingSecondHalfOpen() { return m_pendingSecondHalfOpen; }
- void setPendingSecondHalfOpen(PassOwnPtr<IDBPendingOpenCall> pendingOpenCall) { m_pendingSecondHalfOpen = pendingOpenCall; }
-
- IDBFactoryBackendInterface& factoryBackend() { return *m_factory; }
-
- class VersionChangeOperation;
- class VersionChangeAbortOperation;
-
-private:
- IDBDatabaseBackend(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface*, IDBServerConnection&);
-
- 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 deleteDatabaseAsync(PassRefPtr<IDBCallbacks>);
-
- IDBDatabaseMetadata m_metadata;
-
- String m_identifier;
-
- RefPtr<IDBFactoryBackendInterface> m_factory;
- Ref<IDBServerConnection> m_serverConnection;
-
- OwnPtr<IDBTransactionCoordinator> m_transactionCoordinator;
- RefPtr<IDBTransactionBackend> m_runningVersionChangeTransaction;
-
- typedef HashMap<int64_t, IDBTransactionBackend*> TransactionMap;
- TransactionMap m_transactions;
-
- Deque<OwnPtr<IDBPendingOpenCall>> m_pendingOpenCalls;
- OwnPtr<IDBPendingOpenCall> m_pendingSecondHalfOpen;
-
- Deque<OwnPtr<IDBPendingDeleteCall>> m_pendingDeleteCalls;
- HashSet<RefPtr<IDBCallbacks>> m_deleteCallbacksWaitingCompletion;
-
- typedef ListHashSet<RefPtr<IDBDatabaseCallbacks>> DatabaseCallbacksSet;
- DatabaseCallbacksSet m_databaseCallbacksSet;
-
- bool m_closingConnection;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBDatabaseBackend_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h
deleted file mode 100644
index 7770abf6b..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 IDBDatabaseCallbacks_h
-#define IDBDatabaseCallbacks_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBDatabaseError.h"
-#include "IndexedDB.h"
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class IDBDatabase;
-
-class IDBDatabaseCallbacks : public RefCounted<IDBDatabaseCallbacks> {
-public:
- virtual ~IDBDatabaseCallbacks() { }
-
- virtual void onForcedClose() = 0;
- virtual void onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness) = 0;
-
- virtual void onAbort(int64_t transactionId, PassRefPtr<IDBDatabaseError>) = 0;
- virtual void onComplete(int64_t transactionId) = 0;
-
- virtual void connect(IDBDatabase*) = 0;
-};
-
-} // namespace WebCore
-
-#endif
-
-#endif // IDBDatabaseCallbacks_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp
deleted file mode 100644
index 587c4a756..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 "IDBDatabaseCallbacksImpl.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBDatabase.h"
-
-namespace WebCore {
-
-PassRefPtr<IDBDatabaseCallbacksImpl> IDBDatabaseCallbacksImpl::create()
-{
- return adoptRef(new IDBDatabaseCallbacksImpl());
-}
-
-IDBDatabaseCallbacksImpl::IDBDatabaseCallbacksImpl()
- : m_database(0)
-{
-}
-
-IDBDatabaseCallbacksImpl::~IDBDatabaseCallbacksImpl()
-{
-}
-
-void IDBDatabaseCallbacksImpl::onForcedClose()
-{
- if (m_database)
- m_database->forceClose();
-}
-
-void IDBDatabaseCallbacksImpl::onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness)
-{
- if (m_database)
- m_database->onVersionChange(oldVersion, newVersion, newVersionNullness);
-}
-
-void IDBDatabaseCallbacksImpl::connect(IDBDatabase* database)
-{
- ASSERT(!m_database);
- ASSERT(database);
- m_database = database;
-}
-
-void IDBDatabaseCallbacksImpl::onAbort(int64_t transactionId, PassRefPtr<IDBDatabaseError> error)
-{
- if (m_database)
- m_database->onAbort(transactionId, error);
-}
-
-void IDBDatabaseCallbacksImpl::onComplete(int64_t transactionId)
-{
- if (m_database)
- m_database->onComplete(transactionId);
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h
deleted file mode 100644
index 248332a35..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 IDBDatabaseCallbacksImpl_h
-#define IDBDatabaseCallbacksImpl_h
-
-#include "IDBDatabaseCallbacks.h"
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-class IDBDatabase;
-
-class IDBDatabaseCallbacksImpl final : public IDBDatabaseCallbacks {
-public:
- static PassRefPtr<IDBDatabaseCallbacksImpl> create();
- virtual ~IDBDatabaseCallbacksImpl() override;
-
- // IDBDatabaseCallbacks
- virtual void onForcedClose() override;
- virtual void onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness) override;
-
- virtual void onAbort(int64_t transactionId, PassRefPtr<IDBDatabaseError>) override;
- virtual void onComplete(int64_t transactionId) override;
-
- virtual void connect(IDBDatabase*) override;
-
-private:
- IDBDatabaseCallbacksImpl();
-
- // The initial IDBOpenDBRequest or final IDBDatabase maintains a RefPtr to this
- IDBDatabase* m_database;
-};
-
-} // namespace WebCore
-
-#endif
-
-#endif // IDBDatabaseCallbacksImpl_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h
index 7a939b5b6..a27cda681 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseError.h
@@ -23,11 +23,9 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBDatabaseError_h
-#define IDBDatabaseError_h
+#pragma once
#include "IDBDatabaseException.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/text/WTFString.h>
@@ -37,18 +35,18 @@ namespace WebCore {
class IDBDatabaseError : public RefCounted<IDBDatabaseError> {
public:
- static PassRefPtr<IDBDatabaseError> create(unsigned short code)
+ static Ref<IDBDatabaseError> create(unsigned short code)
{
ASSERT(code >= IDBDatabaseException::IDBDatabaseExceptionOffset);
ASSERT(code < IDBDatabaseException::IDBDatabaseExceptionMax);
- return adoptRef(new IDBDatabaseError(code));
+ return adoptRef(*new IDBDatabaseError(code));
}
- static PassRefPtr<IDBDatabaseError> create(unsigned short code, const String& message)
+ static Ref<IDBDatabaseError> create(unsigned short code, const String& message)
{
ASSERT_WITH_MESSAGE(code >= IDBDatabaseException::IDBDatabaseExceptionOffset, "%d >= %d", code, IDBDatabaseException::IDBDatabaseExceptionOffset);
ASSERT(code < IDBDatabaseException::IDBDatabaseExceptionMax);
- return adoptRef(new IDBDatabaseError(code, message));
+ return adoptRef(*new IDBDatabaseError(code, message));
}
~IDBDatabaseError() { }
@@ -70,6 +68,4 @@ private:
} // namespace WebCore
-#endif
-
-#endif // IDBDatabaseError_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp
index 5f11cca09..84ef23efc 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp
@@ -10,7 +10,7 @@
* 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
+ * 3. Neither the name of Apple 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.
*
@@ -27,10 +27,12 @@
*/
#include "config.h"
+#include "IDBDatabaseException.h"
#if ENABLE(INDEXED_DATABASE)
-#include "IDBDatabaseException.h"
+#include "ExceptionCode.h"
+#include "ExceptionCodeDescription.h"
namespace WebCore {
@@ -77,7 +79,7 @@ bool IDBDatabaseException::initializeDescription(ExceptionCode ec, ExceptionCode
description->typeName = "DOM IDBDatabase";
description->code = entry->code;
- description->type = DOMCoreExceptionType;
+ description->type = IDBDatabaseExceptionType;
description->name = entry ? entry->name : 0;
description->description = entry ? entry->description : 0;
@@ -90,7 +92,7 @@ String IDBDatabaseException::getErrorName(ExceptionCode ec)
const IDBDatabaseExceptionNameDescription* entry = getErrorEntry(ec);
ASSERT(entry);
if (!entry)
- return "UnknownError";
+ return ASCIILiteral("UnknownError");
return entry->name;
}
@@ -100,7 +102,7 @@ String IDBDatabaseException::getErrorDescription(ExceptionCode ec)
const IDBDatabaseExceptionNameDescription* entry = getErrorEntry(ec);
ASSERT(entry);
if (!entry)
- return "Unknown error.";
+ return ASCIILiteral("Unknown error.");
return entry->description;
}
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h
index 15e58af28..9e0ce98b0 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseException.h
@@ -23,8 +23,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBDatabaseException_h
-#define IDBDatabaseException_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
@@ -34,9 +33,9 @@ namespace WebCore {
class IDBDatabaseException : public ExceptionBase {
public:
- static PassRefPtr<IDBDatabaseException> create(const ExceptionCodeDescription& description)
+ static Ref<IDBDatabaseException> create(const ExceptionCodeDescription& description)
{
- return adoptRef(new IDBDatabaseException(description));
+ return adoptRef(*new IDBDatabaseException(description));
}
static const int IDBDatabaseExceptionOffset = 1200;
@@ -85,6 +84,4 @@ private:
} // namespace WebCore
-#endif
-
-#endif // IDBDatabaseException_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp
new file mode 100644
index 000000000..81975a261
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "IDBDatabaseIdentifier.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "FileSystem.h"
+#include "SecurityOrigin.h"
+#include <wtf/Ref.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+IDBDatabaseIdentifier::IDBDatabaseIdentifier(const String& databaseName, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin)
+ : m_databaseName(databaseName)
+ , m_openingOrigin(SecurityOriginData::fromSecurityOrigin(openingOrigin))
+ , m_mainFrameOrigin(SecurityOriginData::fromSecurityOrigin(mainFrameOrigin))
+
+{
+ // The empty string is a valid database name, but a null string is not.
+ ASSERT(!databaseName.isNull());
+}
+
+IDBDatabaseIdentifier IDBDatabaseIdentifier::isolatedCopy() const
+{
+ IDBDatabaseIdentifier identifier;
+
+ identifier.m_databaseName = m_databaseName.isolatedCopy();
+ identifier.m_openingOrigin = m_openingOrigin.isolatedCopy();
+ identifier.m_mainFrameOrigin = m_mainFrameOrigin.isolatedCopy();
+
+ return identifier;
+}
+
+String IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(const String& rootDirectory) const
+{
+ return databaseDirectoryRelativeToRoot(m_mainFrameOrigin, m_openingOrigin, rootDirectory);
+}
+
+String IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(const SecurityOriginData& topLevelOrigin, const SecurityOriginData& openingOrigin, const String& rootDirectory)
+{
+ String mainFrameDirectory = pathByAppendingComponent(rootDirectory, topLevelOrigin.databaseIdentifier());
+
+ // If the opening origin and main frame origins are the same, there is no partitioning.
+ if (openingOrigin == topLevelOrigin)
+ return mainFrameDirectory;
+
+ return pathByAppendingComponent(mainFrameDirectory, openingOrigin.databaseIdentifier());
+}
+
+#if !LOG_DISABLED
+String IDBDatabaseIdentifier::debugString() const
+{
+ return makeString(m_databaseName, "@", m_openingOrigin.debugString(), ":", m_mainFrameOrigin.debugString());
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h
new file mode 100644
index 000000000..dd9f14ffa
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "SecurityOriginData.h"
+#include <wtf/Ref.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class SecurityOrigin;
+
+class IDBDatabaseIdentifier {
+public:
+ IDBDatabaseIdentifier() { }
+ IDBDatabaseIdentifier(WTF::HashTableDeletedValueType)
+ : m_databaseName(WTF::HashTableDeletedValue)
+ {
+ }
+
+ IDBDatabaseIdentifier isolatedCopy() const;
+
+ bool isHashTableDeletedValue() const
+ {
+ return m_databaseName.isHashTableDeletedValue();
+ }
+
+ unsigned hash() const
+ {
+ unsigned nameHash = StringHash::hash(m_databaseName);
+ unsigned openingProtocolHash = StringHash::hash(m_openingOrigin.protocol);
+ unsigned openingHostHash = StringHash::hash(m_openingOrigin.host);
+ unsigned mainFrameProtocolHash = StringHash::hash(m_mainFrameOrigin.protocol);
+ unsigned mainFrameHostHash = StringHash::hash(m_mainFrameOrigin.host);
+
+ unsigned hashCodes[7] = { nameHash, openingProtocolHash, openingHostHash, m_openingOrigin.port.value_or(0), mainFrameProtocolHash, mainFrameHostHash, m_mainFrameOrigin.port.value_or(0) };
+ return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes);
+ }
+
+ IDBDatabaseIdentifier(const String& databaseName, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin);
+
+ bool isValid() const
+ {
+ return !m_databaseName.isNull()
+ && !m_databaseName.isHashTableDeletedValue();
+ }
+
+ bool isEmpty() const
+ {
+ return m_databaseName.isNull();
+ }
+
+ bool operator==(const IDBDatabaseIdentifier& other) const
+ {
+ return other.m_databaseName == m_databaseName
+ && other.m_openingOrigin == m_openingOrigin
+ && other.m_mainFrameOrigin == m_mainFrameOrigin;
+ }
+
+ const String& databaseName() const { return m_databaseName; }
+
+ String databaseDirectoryRelativeToRoot(const String& rootDirectory) const;
+ static String databaseDirectoryRelativeToRoot(const SecurityOriginData& topLevelOrigin, const SecurityOriginData& openingOrigin, const String& rootDirectory);
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBDatabaseIdentifier&);
+
+#if !LOG_DISABLED
+ String debugString() const;
+#endif
+
+ bool isRelatedToOrigin(const SecurityOriginData& other) const
+ {
+ return m_openingOrigin == other || m_mainFrameOrigin == other;
+ }
+
+private:
+ String m_databaseName;
+ SecurityOriginData m_openingOrigin;
+ SecurityOriginData m_mainFrameOrigin;
+};
+
+struct IDBDatabaseIdentifierHash {
+ static unsigned hash(const IDBDatabaseIdentifier& a) { return a.hash(); }
+ static bool equal(const IDBDatabaseIdentifier& a, const IDBDatabaseIdentifier& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+struct IDBDatabaseIdentifierHashTraits : WTF::SimpleClassHashTraits<IDBDatabaseIdentifier> {
+ static const bool hasIsEmptyValueFunction = true;
+ static const bool emptyValueIsZero = false;
+ static bool isEmptyValue(const IDBDatabaseIdentifier& info) { return info.isEmpty(); }
+};
+
+template<class Encoder>
+void IDBDatabaseIdentifier::encode(Encoder& encoder) const
+{
+ encoder << m_databaseName << m_openingOrigin << m_mainFrameOrigin;
+}
+
+template<class Decoder>
+bool IDBDatabaseIdentifier::decode(Decoder& decoder, IDBDatabaseIdentifier& identifier)
+{
+ if (!decoder.decode(identifier.m_databaseName))
+ return false;
+
+ if (!decoder.decode(identifier.m_openingOrigin))
+ return false;
+
+ if (!decoder.decode(identifier.m_mainFrameOrigin))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+namespace WTF {
+
+template<> struct HashTraits<WebCore::IDBDatabaseIdentifier> : WebCore::IDBDatabaseIdentifierHashTraits { };
+template<> struct DefaultHash<WebCore::IDBDatabaseIdentifier> {
+ typedef WebCore::IDBDatabaseIdentifierHash Hash;
+};
+
+} // namespace WTF
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h b/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h
deleted file mode 100644
index 75717440a..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2012 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 GOOGLE 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 IDBDatabaseMetadata_h
-#define IDBDatabaseMetadata_h
-
-#include "IDBObjectStoreMetadata.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-struct IDBDatabaseMetadata {
-
- // FIXME: These are only here to support the LevelDB backend which incorrectly handles versioning.
- // Once LevelDB supports a single, uint64_t version and throws out the old string version, these
- // should be gotten rid of.
- // Also, "NoIntVersion" used to be a magic number of -1, which doesn't work with the new unsigned type.
- // Changing the value to INT64_MAX here seems like a reasonable temporary fix as the current LevelDB
- // already cannot represent valid version numbers between INT64_MAX and UINT64_MAX.
-
-#ifndef INT64_MAX
-#define INT64_MAX 9223372036854775807LL
-#endif
-
- enum {
- NoIntVersion = INT64_MAX,
- DefaultIntVersion = 0
- };
-
- IDBDatabaseMetadata()
- : id(0)
- , version(0)
- , maxObjectStoreId(0)
- {
- }
-
- IDBDatabaseMetadata(const String& name, int64_t id, uint64_t version, int64_t maxObjectStoreId)
- : name(name)
- , id(id)
- , version(version)
- , maxObjectStoreId(maxObjectStoreId)
- {
- }
-
- String name;
- int64_t id;
- uint64_t version;
- int64_t maxObjectStoreId;
-
- typedef HashMap<int64_t, IDBObjectStoreMetadata> ObjectStoreMap;
- ObjectStoreMap objectStores;
-
- IDBDatabaseMetadata isolatedCopy() const;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBDatabaseMetadata_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp
index 116f7d526..e513143b4 100644
--- a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp
@@ -10,7 +10,7 @@
* 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
+ * 3. Neither the name of Apple 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.
*
@@ -36,30 +36,30 @@
namespace WebCore {
-bool IDBEventDispatcher::dispatch(Event* event, Vector<RefPtr<EventTarget>>& eventTargets)
+bool IDBEventDispatcher::dispatch(Event& event, Vector<RefPtr<EventTarget>>& eventTargets)
{
size_t size = eventTargets.size();
ASSERT(size);
- event->setEventPhase(Event::CAPTURING_PHASE);
+ event.setEventPhase(Event::CAPTURING_PHASE);
for (size_t i = size - 1; i; --i) { // Don't do the first element.
- event->setCurrentTarget(eventTargets[i].get());
+ event.setCurrentTarget(eventTargets[i].get());
eventTargets[i]->fireEventListeners(event);
- if (event->propagationStopped())
+ if (event.propagationStopped())
goto doneDispatching;
}
- event->setEventPhase(Event::AT_TARGET);
- event->setCurrentTarget(eventTargets[0].get());
+ event.setEventPhase(Event::AT_TARGET);
+ event.setCurrentTarget(eventTargets[0].get());
eventTargets[0]->fireEventListeners(event);
- if (event->propagationStopped() || !event->bubbles() || event->cancelBubble())
+ if (event.propagationStopped() || !event.bubbles())
goto doneDispatching;
- event->setEventPhase(Event::BUBBLING_PHASE);
+ event.setEventPhase(Event::BUBBLING_PHASE);
for (size_t i = 1; i < size; ++i) { // Don't do the first element.
- event->setCurrentTarget(eventTargets[i].get());
+ event.setCurrentTarget(eventTargets[i].get());
eventTargets[i]->fireEventListeners(event);
- if (event->propagationStopped() || event->cancelBubble())
+ if (event.propagationStopped())
goto doneDispatching;
}
@@ -82,9 +82,9 @@ bool IDBEventDispatcher::dispatch(Event* event, Vector<RefPtr<EventTarget>>& eve
// event on the window until that has been implemented)." -- Jonas Sicking
doneDispatching:
- event->setCurrentTarget(0);
- event->setEventPhase(0);
- return !event->defaultPrevented();
+ event.setCurrentTarget(nullptr);
+ event.setEventPhase(0);
+ return !event.defaultPrevented();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h
index 1d263b560..47e5e9230 100644
--- a/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h
+++ b/Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h
@@ -10,7 +10,7 @@
* 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
+ * 3. Neither the name of Apple 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.
*
@@ -26,12 +26,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBEventDispatcher_h
-#define IDBEventDispatcher_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
-#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
namespace WebCore {
@@ -41,7 +39,7 @@ class EventTarget;
class IDBEventDispatcher {
public:
- static bool dispatch(Event*, Vector<RefPtr<EventTarget>>&); // The target first and then its ancestors in order of how the event bubbles.
+ static bool dispatch(Event&, Vector<RefPtr<EventTarget>>&); // The target first and then its ancestors in order of how the event bubbles.
private:
IDBEventDispatcher();
@@ -50,5 +48,3 @@ private:
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBEventDispatcher_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBFactory.cpp b/Source/WebCore/Modules/indexeddb/IDBFactory.cpp
index 7c73d8449..db9aa0e66 100644
--- a/Source/WebCore/Modules/indexeddb/IDBFactory.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBFactory.cpp
@@ -1,29 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -33,153 +30,113 @@
#include "Document.h"
#include "ExceptionCode.h"
-#include "Frame.h"
-#include "GroupSettings.h"
-#include "HistogramSupport.h"
#include "IDBBindingUtilities.h"
-#include "IDBDatabase.h"
-#include "IDBDatabaseCallbacksImpl.h"
-#include "IDBDatabaseException.h"
-#include "IDBFactoryBackendInterface.h"
-#include "IDBHistograms.h"
+#include "IDBConnectionProxy.h"
+#include "IDBDatabaseIdentifier.h"
#include "IDBKey.h"
-#include "IDBKeyRange.h"
#include "IDBOpenDBRequest.h"
#include "Logging.h"
#include "Page.h"
-#include "PageGroup.h"
+#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
-#include "WorkerGlobalScope.h"
-#include "WorkerLoaderProxy.h"
-#include "WorkerThread.h"
+
+using namespace JSC;
namespace WebCore {
-IDBFactory::IDBFactory(IDBFactoryBackendInterface* factory)
- : m_backend(factory)
+static bool shouldThrowSecurityException(ScriptExecutionContext& context)
{
- // We pass a reference to this object before it can be adopted.
- relaxAdoptionRequirement();
+ ASSERT(is<Document>(context) || context.isWorkerGlobalScope());
+ if (is<Document>(context)) {
+ Document& document = downcast<Document>(context);
+ if (!document.frame())
+ return true;
+ if (!document.page())
+ return true;
+ }
+
+ if (!context.securityOrigin()->canAccessDatabase(context.topOrigin()))
+ return true;
+
+ return false;
}
-IDBFactory::~IDBFactory()
+Ref<IDBFactory> IDBFactory::create(IDBClient::IDBConnectionProxy& connectionProxy)
{
+ return adoptRef(*new IDBFactory(connectionProxy));
}
-namespace {
-static bool isContextValid(ScriptExecutionContext* context)
+IDBFactory::IDBFactory(IDBClient::IDBConnectionProxy& connectionProxy)
+ : m_connectionProxy(connectionProxy)
{
- ASSERT(context->isDocument() || context->isWorkerGlobalScope());
- if (context->isDocument()) {
- Document* document = toDocument(context);
- return document->frame() && document->page();
- }
- return true;
}
-static String getIndexedDBDatabasePath(ScriptExecutionContext* context)
+IDBFactory::~IDBFactory()
{
- ASSERT(isContextValid(context));
- if (context->isDocument()) {
- Document* document = toDocument(context);
- return document->page()->group().groupSettings().indexedDBDatabasePath();
- }
- WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
- const GroupSettings* groupSettings = workerGlobalScope->groupSettings();
- if (groupSettings)
- return groupSettings->indexedDBDatabasePath();
- return String();
-}
}
-PassRefPtr<IDBRequest> IDBFactory::getDatabaseNames(ScriptExecutionContext* context, ExceptionCode& ec)
+ExceptionOr<Ref<IDBOpenDBRequest>> IDBFactory::open(ScriptExecutionContext& context, const String& name, std::optional<uint64_t> version)
{
- LOG(StorageAPI, "IDBFactory::getDatabaseNames");
- if (!isContextValid(context))
- return 0;
- if (!context->securityOrigin()->canAccessDatabase(context->topOrigin())) {
- ec = SECURITY_ERR;
- return 0;
- }
+ LOG(IndexedDB, "IDBFactory::open");
+
+ if (version && !version.value())
+ return Exception { TypeError, ASCIILiteral("IDBFactory.open() called with a version of 0") };
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), 0);
- m_backend->getDatabaseNames(request, context->securityOrigin(), context, getIndexedDBDatabasePath(context));
- return request;
+ return openInternal(context, name, version.value_or(0));
}
-PassRefPtr<IDBOpenDBRequest> IDBFactory::open(ScriptExecutionContext* context, const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<IDBOpenDBRequest>> IDBFactory::openInternal(ScriptExecutionContext& context, const String& name, uint64_t version)
{
- LOG(StorageAPI, "IDBFactory::open");
- return openInternal(context, name, 0, IndexedDB::VersionNullness::Null, ec);
-}
+ if (name.isNull())
+ return Exception { TypeError, ASCIILiteral("IDBFactory.open() called without a database name") };
-PassRefPtr<IDBOpenDBRequest> IDBFactory::open(ScriptExecutionContext* context, const String& name, unsigned long long version, ExceptionCode& ec)
-{
- LOG(StorageAPI, "IDBFactory::open");
- if (!version) {
- ec = TypeError;
- return 0;
- }
- return openInternal(context, name, version, IndexedDB::VersionNullness::NonNull, ec);
-}
+ if (shouldThrowSecurityException(context))
+ return Exception { SECURITY_ERR, ASCIILiteral("IDBFactory.open() called in an invalid security context") };
-PassRefPtr<IDBOpenDBRequest> IDBFactory::openInternal(ScriptExecutionContext* context, const String& name, uint64_t version, IndexedDB::VersionNullness versionNullness, ExceptionCode& ec)
-{
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBOpenCall, IDBMethodsMax);
- ASSERT(version >= 1 || versionNullness == IndexedDB::VersionNullness::Null);
- if (name.isNull()) {
- ec = TypeError;
- return 0;
- }
- if (!isContextValid(context))
- return 0;
- if (!context->securityOrigin()->canAccessDatabase(context->topOrigin())) {
- ec = SECURITY_ERR;
- return 0;
- }
+ ASSERT(context.securityOrigin());
+ IDBDatabaseIdentifier databaseIdentifier(name, *context.securityOrigin(), context.topOrigin());
+ if (!databaseIdentifier.isValid())
+ return Exception { TypeError, ASCIILiteral("IDBFactory.open() called with an invalid security origin") };
- RefPtr<IDBDatabaseCallbacks> databaseCallbacks = IDBDatabaseCallbacksImpl::create();
- int64_t transactionId = IDBDatabase::nextTransactionId();
- RefPtr<IDBOpenDBRequest> request = IDBOpenDBRequest::create(context, databaseCallbacks, transactionId, version, versionNullness);
- m_backend->open(name, version, transactionId, request, databaseCallbacks, *(context->securityOrigin()), *(context->topOrigin()));
- return request;
+ LOG(IndexedDBOperations, "IDB opening database: %s %" PRIu64, name.utf8().data(), version);
+
+ return m_connectionProxy->openDatabase(context, databaseIdentifier, version);
}
-PassRefPtr<IDBOpenDBRequest> IDBFactory::deleteDatabase(ScriptExecutionContext* context, const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<IDBOpenDBRequest>> IDBFactory::deleteDatabase(ScriptExecutionContext& context, const String& name)
{
- LOG(StorageAPI, "IDBFactory::deleteDatabase");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.FrontEndAPICalls", IDBDeleteDatabaseCall, IDBMethodsMax);
- if (name.isNull()) {
- ec = TypeError;
- return 0;
- }
- if (!isContextValid(context))
- return 0;
- if (!context->securityOrigin()->canAccessDatabase(context->topOrigin())) {
- ec = SECURITY_ERR;
- return 0;
- }
+ LOG(IndexedDB, "IDBFactory::deleteDatabase - %s", name.utf8().data());
- RefPtr<IDBOpenDBRequest> request = IDBOpenDBRequest::create(context, 0, 0, 0, IndexedDB::VersionNullness::Null);
- m_backend->deleteDatabase(name, request, context->securityOrigin(), context, getIndexedDBDatabasePath(context));
- return request;
+ if (name.isNull())
+ return Exception { TypeError, ASCIILiteral("IDBFactory.deleteDatabase() called without a database name") };
+
+ if (shouldThrowSecurityException(context))
+ return Exception { SECURITY_ERR, ASCIILiteral("IDBFactory.deleteDatabase() called in an invalid security context") };
+
+ ASSERT(context.securityOrigin());
+ IDBDatabaseIdentifier databaseIdentifier(name, *context.securityOrigin(), context.topOrigin());
+ if (!databaseIdentifier.isValid())
+ return Exception { TypeError, ASCIILiteral("IDBFactory.deleteDatabase() called with an invalid security origin") };
+
+ LOG(IndexedDBOperations, "IDB deleting database: %s", name.utf8().data());
+
+ return m_connectionProxy->deleteDatabase(context, databaseIdentifier);
}
-short IDBFactory::cmp(ScriptExecutionContext* context, const Deprecated::ScriptValue& firstValue, const Deprecated::ScriptValue& secondValue, ExceptionCode& ec)
+ExceptionOr<short> IDBFactory::cmp(ExecState& execState, JSValue firstValue, JSValue secondValue)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> first = scriptValueToIDBKey(&requestState, firstValue);
- RefPtr<IDBKey> second = scriptValueToIDBKey(&requestState, secondValue);
+ auto first = scriptValueToIDBKey(execState, firstValue);
+ auto second = scriptValueToIDBKey(execState, secondValue);
- ASSERT(first);
- ASSERT(second);
+ if (!first->isValid() || !second->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'cmp' on 'IDBFactory': The parameter is not a valid key.") };
- if (!first->isValid() || !second->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ return first->compare(second.get());
+}
- return static_cast<short>(first->compare(second.get()));
+void IDBFactory::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function<void (const Vector<String>&)> callback)
+{
+ m_connectionProxy->getAllDatabaseNames(mainFrameOrigin, openingOrigin, callback);
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBFactory.h b/Source/WebCore/Modules/indexeddb/IDBFactory.h
index 778fd54e4..8dbc11ce6 100644
--- a/Source/WebCore/Modules/indexeddb/IDBFactory.h
+++ b/Source/WebCore/Modules/indexeddb/IDBFactory.h
@@ -1,83 +1,74 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBFactory_h
-#define IDBFactory_h
-#include "IDBOpenDBRequest.h"
-#include "ScriptWrappable.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "ExceptionOr.h"
+#include <functional>
+#include <wtf/Forward.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+class ExecState;
+class JSValue;
+}
+
namespace WebCore {
-class IDBKey;
-class IDBKeyRange;
-class IDBFactoryBackendInterface;
+class IDBOpenDBRequest;
class ScriptExecutionContext;
+class SecurityOrigin;
-typedef int ExceptionCode;
+namespace IDBClient {
+class IDBConnectionProxy;
+}
-class IDBFactory : public ScriptWrappable, public RefCounted<IDBFactory> {
+class IDBFactory : public ThreadSafeRefCounted<IDBFactory> {
public:
- static PassRefPtr<IDBFactory> create(IDBFactoryBackendInterface* factory)
- {
- // FIXME: While the feature is under development we'll handle a null factory backend here,
- // returning null, so javascript can't try to use the feature.
- // Once the feature is fully functional we should remove the null factory backend.
- return factory ? adoptRef(new IDBFactory(factory)) : nullptr;
- }
+ static Ref<IDBFactory> create(IDBClient::IDBConnectionProxy&);
~IDBFactory();
- // FIXME: getDatabaseNames is no longer a web-facing API, and should be removed from IDBFactory.
- // The Web Inspector currently uses this to enumerate the list of databases, but is more complicated as a result.
- // We should provide a simpler API to the Web Inspector then remove getDatabaseNames.
- PassRefPtr<IDBRequest> getDatabaseNames(ScriptExecutionContext*, ExceptionCode&);
+ ExceptionOr<Ref<IDBOpenDBRequest>> open(ScriptExecutionContext&, const String& name, std::optional<uint64_t> version);
+ ExceptionOr<Ref<IDBOpenDBRequest>> deleteDatabase(ScriptExecutionContext&, const String& name);
- PassRefPtr<IDBOpenDBRequest> open(ScriptExecutionContext*, const String& name, ExceptionCode&);
- PassRefPtr<IDBOpenDBRequest> open(ScriptExecutionContext*, const String& name, unsigned long long version, ExceptionCode&);
- PassRefPtr<IDBOpenDBRequest> deleteDatabase(ScriptExecutionContext*, const String& name, ExceptionCode&);
+ ExceptionOr<short> cmp(JSC::ExecState&, JSC::JSValue first, JSC::JSValue second);
- short cmp(ScriptExecutionContext*, const Deprecated::ScriptValue& first, const Deprecated::ScriptValue& second, ExceptionCode&);
+ WEBCORE_EXPORT void getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function<void (const Vector<String>&)>);
private:
- IDBFactory(IDBFactoryBackendInterface*);
+ explicit IDBFactory(IDBClient::IDBConnectionProxy&);
- PassRefPtr<IDBOpenDBRequest> openInternal(ScriptExecutionContext*, const String& name, uint64_t version, IndexedDB::VersionNullness, ExceptionCode&);
+ ExceptionOr<Ref<IDBOpenDBRequest>> openInternal(ScriptExecutionContext&, const String& name, uint64_t version);
- RefPtr<IDBFactoryBackendInterface> m_backend;
+ Ref<IDBClient::IDBConnectionProxy> m_connectionProxy;
};
} // namespace WebCore
-#endif
-
-#endif // IDBFactory_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBFactory.idl b/Source/WebCore/Modules/indexeddb/IDBFactory.idl
index beb7abd72..d0f63282a 100644
--- a/Source/WebCore/Modules/indexeddb/IDBFactory.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBFactory.idl
@@ -25,11 +25,9 @@
[
Conditional=INDEXED_DATABASE,
- JSNoStaticTables,
- ImplementationLacksVTable,
+ SkipVTableValidation,
] interface IDBFactory {
- [CallWith=ScriptExecutionContext, RaisesException] IDBOpenDBRequest open(DOMString name, [EnforceRange] optional unsigned long long version);
- [CallWith=ScriptExecutionContext, RaisesException] IDBOpenDBRequest deleteDatabase(DOMString name);
- [CallWith=ScriptExecutionContext, RaisesException] short cmp(any first, any second);
+ [CallWith=ScriptExecutionContext, MayThrowException] IDBOpenDBRequest open(DOMString name, [EnforceRange] optional unsigned long long version);
+ [CallWith=ScriptExecutionContext, MayThrowException] IDBOpenDBRequest deleteDatabase(DOMString name);
+ [CallWith=ScriptState, MayThrowException] short cmp(any first, any second);
};
-
diff --git a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp b/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp
deleted file mode 100644
index d1c9fbde5..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp
+++ /dev/null
@@ -1,45 +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 "IDBFactoryBackendInterface.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "DatabaseStrategy.h"
-#include "PlatformStrategies.h"
-
-namespace WebCore {
-
-PassRefPtr<IDBFactoryBackendInterface> IDBFactoryBackendInterface::create(const String& databaseDirectoryIdentifier)
-{
- return platformStrategies()->databaseStrategy()->createIDBFactoryBackend(databaseDirectoryIdentifier);
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h b/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h
deleted file mode 100644
index 3c7c59306..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h
+++ /dev/null
@@ -1,74 +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 IDBFactoryBackendInterface_h
-#define IDBFactoryBackendInterface_h
-
-#include "IDBDatabaseBackend.h"
-#include "IndexedDB.h"
-
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-class IDBCallbacks;
-class IDBCursorBackend;
-class IDBDatabase;
-class IDBDatabaseBackend;
-class IDBDatabaseCallbacks;
-class IDBTransactionBackend;
-class SecurityOrigin;
-class ScriptExecutionContext;
-
-typedef int ExceptionCode;
-
-// This class is shared by IDBFactory (async) and IDBFactorySync (sync).
-// This is implemented by IDBFactoryBackendImpl and optionally others (in order to proxy
-// calls across process barriers). All calls to these classes should be non-blocking and
-// trigger work on a background thread if necessary.
-class IDBFactoryBackendInterface : public RefCounted<IDBFactoryBackendInterface> {
-public:
- static PassRefPtr<IDBFactoryBackendInterface> create(const String& databaseDirectoryIdentifier);
- virtual ~IDBFactoryBackendInterface() { }
-
- virtual void getDatabaseNames(PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, ScriptExecutionContext*, const String& dataDir) = 0;
- virtual void open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin) = 0;
- virtual void deleteDatabase(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, ScriptExecutionContext*, const String& dataDir) = 0;
-
- virtual void removeIDBDatabaseBackend(const String& uniqueIdentifier) = 0;
-
-};
-
-} // namespace WebCore
-
-#endif // IDBFactoryBackendInterface_h
-
-#endif // IDBFactoryBackendInterface_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp
new file mode 100644
index 000000000..4a45a37a2
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBGetAllResult.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+template<typename T> void isolatedCopyOfVariant(const WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t>& source, WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t>& target)
+{
+ target = Vector<T>();
+ auto& sourceVector = WTF::get<Vector<T>>(source);
+ auto& targetVector = WTF::get<Vector<T>>(target);
+ targetVector.reserveInitialCapacity(sourceVector.size());
+ for (auto& element : sourceVector)
+ targetVector.uncheckedAppend(element.isolatedCopy());
+}
+
+IDBGetAllResult::IDBGetAllResult(const IDBGetAllResult& that, IsolatedCopyTag)
+{
+ isolatedCopy(that, *this);
+}
+
+IDBGetAllResult IDBGetAllResult::isolatedCopy() const
+{
+ return { *this, IsolatedCopy };
+}
+
+void IDBGetAllResult::isolatedCopy(const IDBGetAllResult& source, IDBGetAllResult& destination)
+{
+ destination.m_type = source.m_type;
+
+ if (WTF::holds_alternative<std::nullptr_t>(source.m_results))
+ return;
+
+ switch (source.m_type) {
+ case IndexedDB::GetAllType::Keys:
+ isolatedCopyOfVariant<IDBKeyData>(source.m_results, destination.m_results);
+ break;
+ case IndexedDB::GetAllType::Values:
+ isolatedCopyOfVariant<IDBValue>(source.m_results, destination.m_results);
+ break;
+ }
+}
+
+void IDBGetAllResult::addKey(IDBKeyData&& key)
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Keys);
+ ASSERT(WTF::holds_alternative<Vector<IDBKeyData>>(m_results));
+ WTF::get<Vector<IDBKeyData>>(m_results).append(WTFMove(key));
+}
+
+void IDBGetAllResult::addValue(IDBValue&& value)
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Values);
+ ASSERT(WTF::holds_alternative<Vector<IDBValue>>(m_results));
+ WTF::get<Vector<IDBValue>>(m_results).append(WTFMove(value));
+}
+
+const Vector<IDBKeyData>& IDBGetAllResult::keys() const
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Keys);
+ ASSERT(WTF::holds_alternative<Vector<IDBKeyData>>(m_results));
+ return WTF::get<Vector<IDBKeyData>>(m_results);
+}
+
+const Vector<IDBValue>& IDBGetAllResult::values() const
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Values);
+ ASSERT(WTF::holds_alternative<Vector<IDBValue>>(m_results));
+ return WTF::get<Vector<IDBValue>>(m_results);
+}
+
+Vector<String> IDBGetAllResult::allBlobFilePaths() const
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Values);
+
+ HashSet<String> pathSet;
+ for (auto& value : WTF::get<Vector<IDBValue>>(m_results)) {
+ for (auto& path : value.blobFilePaths())
+ pathSet.add(path);
+ }
+
+ Vector<String> paths;
+ copyToVector(pathSet, paths);
+
+ return paths;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBGetAllResult.h b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.h
new file mode 100644
index 000000000..ef7c2578e
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBGetAllResult.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyData.h"
+#include "IDBValue.h"
+#include "IndexedDB.h"
+
+#include <wtf/Variant.h>
+
+namespace WebCore {
+
+class IDBGetAllResult {
+public:
+ IDBGetAllResult()
+ {
+ }
+
+ IDBGetAllResult(IndexedDB::GetAllType type)
+ : m_type(type)
+ {
+ switch (m_type) {
+ case IndexedDB::GetAllType::Keys:
+ m_results = Vector<IDBKeyData>();
+ break;
+ case IndexedDB::GetAllType::Values:
+ m_results = Vector<IDBValue>();
+ break;
+ }
+ }
+
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBGetAllResult(const IDBGetAllResult&, IsolatedCopyTag);
+ IDBGetAllResult isolatedCopy() const;
+
+ IndexedDB::GetAllType type() const { return m_type; }
+ const Vector<IDBKeyData>& keys() const;
+ const Vector<IDBValue>& values() const;
+
+ void addKey(IDBKeyData&&);
+ void addValue(IDBValue&&);
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBGetAllResult&);
+
+ WEBCORE_EXPORT Vector<String> allBlobFilePaths() const;
+
+private:
+ static void isolatedCopy(const IDBGetAllResult& source, IDBGetAllResult& destination);
+
+ IndexedDB::GetAllType m_type { IndexedDB::GetAllType::Keys };
+ WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t> m_results { nullptr };
+};
+
+template<class Encoder>
+void IDBGetAllResult::encode(Encoder& encoder) const
+{
+ encoder << m_type << static_cast<uint64_t>(m_results.index());
+
+ switch (m_results.index()) {
+ case 0:
+ encoder << WTF::get<Vector<IDBKeyData>>(m_results);
+ break;
+ case 1:
+ encoder << WTF::get<Vector<IDBValue>>(m_results);
+ break;
+ case 2:
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+template<class Decoder>
+bool IDBGetAllResult::decode(Decoder& decoder, IDBGetAllResult& result)
+{
+ if (!decoder.decode(result.m_type))
+ return false;
+
+ uint64_t index;
+ if (!decoder.decode(index))
+ return false;
+
+ switch (index) {
+ case 0:
+ result.m_results = Vector<IDBKeyData>();
+ if (!decoder.decode(WTF::get<Vector<IDBKeyData>>(result.m_results)))
+ return false;
+ break;
+ case 1:
+ result.m_results = Vector<IDBValue>();
+ if (!decoder.decode(WTF::get<Vector<IDBValue>>(result.m_results)))
+ return false;
+ break;
+ case 2:
+ result.m_results = nullptr;
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBGetResult.cpp b/Source/WebCore/Modules/indexeddb/IDBGetResult.cpp
new file mode 100644
index 000000000..aa36451f2
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBGetResult.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBGetResult.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+void IDBGetResult::dataFromBuffer(SharedBuffer& buffer)
+{
+ Vector<uint8_t> data(buffer.size());
+ memcpy(data.data(), buffer.data(), buffer.size());
+
+ m_value = ThreadSafeDataBuffer::adoptVector(data);
+}
+
+IDBGetResult::IDBGetResult(const IDBGetResult& that, IsolatedCopyTag)
+{
+ isolatedCopy(that, *this);
+}
+
+IDBGetResult IDBGetResult::isolatedCopy() const
+{
+ return { *this, IsolatedCopy };
+}
+
+void IDBGetResult::isolatedCopy(const IDBGetResult& source, IDBGetResult& destination)
+{
+ destination.m_value = source.m_value.isolatedCopy();
+ destination.m_keyData = source.m_keyData.isolatedCopy();
+ destination.m_primaryKeyData = source.m_primaryKeyData.isolatedCopy();
+ destination.m_keyPath = WebCore::isolatedCopy(source.m_keyPath);
+ destination.m_isDefined = source.m_isDefined;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBGetResult.h b/Source/WebCore/Modules/indexeddb/IDBGetResult.h
index 04ae0f4ae..6224464ef 100644
--- a/Source/WebCore/Modules/indexeddb/IDBGetResult.h
+++ b/Source/WebCore/Modules/indexeddb/IDBGetResult.h
@@ -23,57 +23,132 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBGetResult_h
-#define IDBGetResult_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
#include "IDBKey.h"
#include "IDBKeyData.h"
#include "IDBKeyPath.h"
+#include "IDBValue.h"
#include "SharedBuffer.h"
namespace WebCore {
-struct IDBGetResult {
+class IDBGetResult {
+public:
IDBGetResult()
+ : m_isDefined(false)
{
}
- IDBGetResult(PassRefPtr<SharedBuffer> buffer)
- : valueBuffer(buffer)
+ IDBGetResult(const IDBValue& value, const IDBKeyData& currentPrimaryKey)
+ : m_value(value)
+ , m_primaryKeyData(currentPrimaryKey)
{
}
- IDBGetResult(PassRefPtr<IDBKey> key)
- : keyData(key.get())
+ IDBGetResult(const ThreadSafeDataBuffer& buffer)
+ : m_value(buffer)
{
}
- IDBGetResult(PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey> key, const IDBKeyPath& path)
- : valueBuffer(buffer)
- , keyData(key.get())
- , keyPath(path)
+ IDBGetResult(IDBValue&& buffer)
+ : m_value(WTFMove(buffer))
{
}
- IDBGetResult isolatedCopy() const
+ IDBGetResult(IDBKey& key)
+ : m_keyData(&key)
{
- IDBGetResult result;
- if (valueBuffer)
- result.valueBuffer = valueBuffer->copy();
+ }
+
+ IDBGetResult(const IDBKeyData& keyData)
+ : m_keyData(keyData)
+ {
+ }
+
+ IDBGetResult(SharedBuffer* buffer, IDBKey& key, const IDBKeyPath& path)
+ : m_keyData(&key)
+ , m_keyPath(path)
+ {
+ if (buffer)
+ dataFromBuffer(*buffer);
+ }
- result.keyData = keyData.isolatedCopy();
- result.keyPath = keyPath.isolatedCopy();
- return result;
+ IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData)
+ : m_keyData(keyData)
+ , m_primaryKeyData(primaryKeyData)
+ {
+ }
+
+ IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, IDBValue&& value)
+ : m_value(WTFMove(value))
+ , m_keyData(keyData)
+ , m_primaryKeyData(primaryKeyData)
+ {
+ }
+
+ IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, const IDBValue& value)
+ : m_value(value)
+ , m_keyData(keyData)
+ , m_primaryKeyData(primaryKeyData)
+ {
}
- RefPtr<SharedBuffer> valueBuffer;
- IDBKeyData keyData;
- IDBKeyPath keyPath;
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBGetResult(const IDBGetResult&, IsolatedCopyTag);
+
+ IDBGetResult isolatedCopy() const;
+
+ const IDBValue& value() const { return m_value; }
+ const IDBKeyData& keyData() const { return m_keyData; }
+ const IDBKeyData& primaryKeyData() const { return m_primaryKeyData; }
+ const IDBKeyPath& keyPath() const { return m_keyPath; }
+ bool isDefined() const { return m_isDefined; }
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBGetResult&);
+
+private:
+ void dataFromBuffer(SharedBuffer&);
+
+ static void isolatedCopy(const IDBGetResult& source, IDBGetResult& destination);
+
+ IDBValue m_value;
+ IDBKeyData m_keyData;
+ IDBKeyData m_primaryKeyData;
+ IDBKeyPath m_keyPath;
+ bool m_isDefined { true };
};
+template<class Encoder>
+void IDBGetResult::encode(Encoder& encoder) const
+{
+ encoder << m_keyData << m_primaryKeyData << m_keyPath << m_isDefined << m_value;
+}
+
+template<class Decoder>
+bool IDBGetResult::decode(Decoder& decoder, IDBGetResult& result)
+{
+ if (!decoder.decode(result.m_keyData))
+ return false;
+
+ if (!decoder.decode(result.m_primaryKeyData))
+ return false;
+
+ if (!decoder.decode(result.m_keyPath))
+ return false;
+
+ if (!decoder.decode(result.m_isDefined))
+ return false;
+
+ if (!decoder.decode(result.m_value))
+ return false;
+
+ return true;
+}
+
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBGetResult_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBHistograms.h b/Source/WebCore/Modules/indexeddb/IDBHistograms.h
deleted file mode 100644
index d58f088bd..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBHistograms.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 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 GOOGLE 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 IDBHistograms_h
-#define IDBHistograms_h
-
-namespace WebCore {
-
-enum IndexedDatabaseMethods {
- IDBCreateObjectStoreCall,
- IDBDeleteObjectStoreCall,
- IDBTransactionCall,
- IDBDeleteDatabaseCall,
- IDBOpenCall,
- IDBMethodsMax,
-};
-
-}
-
-#endif
diff --git a/Source/WebCore/Modules/indexeddb/IDBIndex.cpp b/Source/WebCore/Modules/indexeddb/IDBIndex.cpp
index e6fdf5cb8..3ec51603c 100644
--- a/Source/WebCore/Modules/indexeddb/IDBIndex.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBIndex.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -28,181 +28,354 @@
#if ENABLE(INDEXED_DATABASE)
+#include "ExceptionCode.h"
+#include "IDBBindingUtilities.h"
+#include "IDBCursor.h"
+#include "IDBDatabase.h"
#include "IDBDatabaseException.h"
-#include "IDBKey.h"
-#include "IDBKeyRange.h"
+#include "IDBKeyRangeData.h"
#include "IDBObjectStore.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
#include "Logging.h"
-#include "ScriptExecutionContext.h"
+#include <heap/HeapInlines.h>
+
+using namespace JSC;
namespace WebCore {
-IDBIndex::IDBIndex(const IDBIndexMetadata& metadata, IDBObjectStore* objectStore, IDBTransaction* transaction)
- : m_metadata(metadata)
+IDBIndex::IDBIndex(ScriptExecutionContext& context, const IDBIndexInfo& info, IDBObjectStore& objectStore)
+ : ActiveDOMObject(&context)
+ , m_info(info)
+ , m_originalInfo(info)
, m_objectStore(objectStore)
- , m_transaction(transaction)
- , m_deleted(false)
{
- ASSERT(m_objectStore);
- ASSERT(m_transaction);
- ASSERT(m_metadata.id != IDBIndexMetadata::InvalidId);
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ suspendIfNeeded();
}
IDBIndex::~IDBIndex()
{
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
}
-PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, const String& directionString, ExceptionCode& ec)
+const char* IDBIndex::activeDOMObjectName() const
{
- LOG(StorageAPI, "IDBIndex::openCursor");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec);
- if (ec)
- return 0;
+ return "IDBIndex";
+}
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- request->setCursorDetails(IndexedDB::CursorType::KeyAndValue, direction);
- backendDB()->openCursor(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, direction, false, IDBDatabaseBackend::NormalTask, request);
- return request;
+bool IDBIndex::canSuspendForDocumentSuspension() const
+{
+ return false;
}
-PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
+bool IDBIndex::hasPendingActivity() const
{
- LOG(StorageAPI, "IDBIndex::openCursor");
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return openCursor(context, keyRange.release(), direction, ec);
+ return !m_objectStore.transaction().isFinished();
}
-PassRefPtr<IDBRequest> IDBIndex::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+const String& IDBIndex::name() const
{
- LOG(StorageAPI, "IDBIndex::count");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->count(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, request);
- return request;
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+ return m_info.name();
}
-PassRefPtr<IDBRequest> IDBIndex::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<void> IDBIndex::setName(const String& name)
{
- LOG(StorageAPI, "IDBIndex::count");
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return count(context, keyRange.release(), ec);
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted)
+ return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index has been deleted.") };
+
+ if (m_objectStore.isDeleted())
+ return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index's object store has been deleted.") };
+
+ if (!m_objectStore.transaction().isVersionChange())
+ return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index's transaction is not a version change transaction.") };
+
+ if (!m_objectStore.transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed set property 'name' on 'IDBIndex': The index's transaction is not active.") };
+
+ if (m_info.name() == name)
+ return { };
+
+ if (m_objectStore.info().hasIndex(name))
+ return Exception { IDBDatabaseException::ConstraintError, makeString("Failed set property 'name' on 'IDBIndex': The owning object store already has an index named '", name, "'.") };
+
+ m_objectStore.transaction().database().renameIndex(*this, name);
+ m_info.rename(name);
+
+ return { };
}
-PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, const String& directionString, ExceptionCode& ec)
+IDBObjectStore& IDBIndex::objectStore()
{
- LOG(StorageAPI, "IDBIndex::openKeyCursor");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec);
- if (ec)
- return 0;
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+ return m_objectStore;
+}
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- request->setCursorDetails(IndexedDB::CursorType::KeyOnly, direction);
- backendDB()->openCursor(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, direction, true, IDBDatabaseBackend::NormalTask, request);
- return request;
+const IDBKeyPath& IDBIndex::keyPath() const
+{
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+ return m_info.keyPath();
}
-PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
+bool IDBIndex::unique() const
{
- LOG(StorageAPI, "IDBIndex::openKeyCursor");
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return openKeyCursor(context, keyRange.release(), direction, ec);
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+ return m_info.unique();
}
-PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+bool IDBIndex::multiEntry() const
{
- LOG(StorageAPI, "IDBIndex::get");
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return get(context, keyRange.release(), ec);
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+ return m_info.multiEntry();
}
-PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+void IDBIndex::rollbackInfoForVersionChangeAbort()
{
- LOG(StorageAPI, "IDBIndex::get");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (!keyRange) {
- ec = IDBDatabaseException::DataError;
- return 0;
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ // Only rollback to the original info if this index still exists in the rolled-back database info.
+ auto* objectStoreInfo = m_objectStore.transaction().database().info().infoForExistingObjectStore(m_objectStore.info().identifier());
+ if (!objectStoreInfo)
+ return;
+
+ if (!objectStoreInfo->hasIndex(m_info.identifier())) {
+ m_deleted = true;
+ return;
}
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->get(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, false, request);
- return request;
+ m_info = m_originalInfo;
+ m_deleted = false;
}
-PassRefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBIndex::openCursor(ExecState& execState, IDBKeyRange* range, IDBCursorDirection direction)
{
- LOG(StorageAPI, "IDBIndex::getKey");
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
+ LOG(IndexedDB, "IDBIndex::openCursor");
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted || m_objectStore.isDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The index or its object store has been deleted.") };
+
+ if (!m_objectStore.transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The transaction is inactive or finished.") };
- return getKey(context, keyRange.release(), ec);
+ IDBKeyRangeData rangeData = range;
+ if (rangeData.lowerKey.isNull())
+ rangeData.lowerKey = IDBKeyData::minimum();
+ if (rangeData.upperKey.isNull())
+ rangeData.upperKey = IDBKeyData::maximum();
+
+ auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), rangeData, direction, IndexedDB::CursorType::KeyAndValue);
+ return m_objectStore.transaction().requestOpenCursor(execState, *this, info);
}
-PassRefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBIndex::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
{
- LOG(StorageAPI, "IDBIndex::getKey");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (!keyRange) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ LOG(IndexedDB, "IDBIndex::openCursor");
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ auto keyRange = IDBKeyRange::only(execState, key);
+ if (keyRange.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The parameter is not a valid key.") };
+
+ return openCursor(execState, keyRange.releaseReturnValue().ptr(), direction);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::openKeyCursor(ExecState& execState, IDBKeyRange* range, IDBCursorDirection direction)
+{
+ LOG(IndexedDB, "IDBIndex::openKeyCursor");
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted || m_objectStore.isDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The index or its object store has been deleted.") };
+
+ if (!m_objectStore.transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The transaction is inactive or finished.") };
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->get(m_transaction->id(), m_objectStore->id(), m_metadata.id, keyRange, true, request);
- return request;
+ auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), range, direction, IndexedDB::CursorType::KeyOnly);
+ return m_objectStore.transaction().requestOpenCursor(execState, *this, info);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
+{
+ LOG(IndexedDB, "IDBIndex::openKeyCursor");
+
+ auto keyRange = IDBKeyRange::only(execState, key);
+ if (keyRange.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The parameter is not a valid key.") };
+ return openKeyCursor(execState, keyRange.releaseReturnValue().ptr(), direction);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::count(ExecState& execState, IDBKeyRange* range)
+{
+ LOG(IndexedDB, "IDBIndex::count");
+
+ return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::count(ExecState& execState, JSValue key)
+{
+ LOG(IndexedDB, "IDBIndex::count");
+
+ auto idbKey = scriptValueToIDBKey(execState, key);
+ if (!idbKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The parameter is not a valid key.") };
+
+ return doCount(execState, IDBKeyRangeData(idbKey.ptr()));
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::doCount(ExecState& execState, const IDBKeyRangeData& range)
+{
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted || m_objectStore.isDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The index or its object store has been deleted.") };
+
+ if (!range.isValid())
+ return Exception { IDBDatabaseException::DataError };
+
+ auto& transaction = m_objectStore.transaction();
+ if (!transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The transaction is inactive or finished.") };
+
+ return transaction.requestCount(execState, *this, range);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::get(ExecState& execState, IDBKeyRange* range)
+{
+ LOG(IndexedDB, "IDBIndex::get");
+
+ return doGet(execState, IDBKeyRangeData(range));
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::get(ExecState& execState, JSValue key)
+{
+ LOG(IndexedDB, "IDBIndex::get");
+
+ auto idbKey = scriptValueToIDBKey(execState, key);
+ if (!idbKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The parameter is not a valid key.") };
+
+ return doGet(execState, IDBKeyRangeData(idbKey.ptr()));
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::doGet(ExecState& execState, const IDBKeyRangeData& range)
+{
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted || m_objectStore.isDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The index or its object store has been deleted.") };
+
+ if (range.isNull)
+ return Exception { IDBDatabaseException::DataError };
+
+ auto& transaction = m_objectStore.transaction();
+ if (!transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The transaction is inactive or finished.") };
+
+ return transaction.requestGetValue(execState, *this, range);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::getKey(ExecState& execState, IDBKeyRange* range)
+{
+ LOG(IndexedDB, "IDBIndex::getKey");
+
+ return doGetKey(execState, IDBKeyRangeData(range));
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::getKey(ExecState& execState, JSValue key)
+{
+ LOG(IndexedDB, "IDBIndex::getKey");
+
+ auto idbKey = scriptValueToIDBKey(execState, key);
+ if (!idbKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The parameter is not a valid key.") };
+
+ return doGetKey(execState, IDBKeyRangeData(idbKey.ptr()));
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::doGetKey(ExecState& execState, const IDBKeyRangeData& range)
+{
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted || m_objectStore.isDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The index or its object store has been deleted.") };
+
+ if (range.isNull)
+ return Exception { IDBDatabaseException::DataError };
+
+ auto& transaction = m_objectStore.transaction();
+ if (!transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The transaction is inactive or finished.") };
+
+ return transaction.requestGetKey(execState, *this, range);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> count)
+{
+ LOG(IndexedDB, "IDBIndex::getAll");
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted || m_objectStore.isDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAll' on 'IDBIndex': The index or its object store has been deleted.") };
+
+ if (!m_objectStore.transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAll' on 'IDBIndex': The transaction is inactive or finished.") };
+
+ return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, range.get(), IndexedDB::GetAllType::Values, count);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, JSValue key, std::optional<uint32_t> count)
+{
+ auto onlyResult = IDBKeyRange::only(execState, key);
+ if (onlyResult.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAll' on 'IDBIndex': The parameter is not a valid key.") };
+
+ return getAll(execState, onlyResult.releaseReturnValue(), count);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> count)
+{
+ LOG(IndexedDB, "IDBIndex::getAllKeys");
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ if (m_deleted || m_objectStore.isDeleted())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBIndex': The index or its object store has been deleted.") };
+
+ if (!m_objectStore.transaction().isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBIndex': The transaction is inactive or finished.") };
+
+ return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, range.get(), IndexedDB::GetAllType::Keys, count);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, JSValue key, std::optional<uint32_t> count)
+{
+ auto onlyResult = IDBKeyRange::only(execState, key);
+ if (onlyResult.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBIndex': The parameter is not a valid key.") };
+
+ return getAllKeys(execState, onlyResult.releaseReturnValue(), count);
+}
+
+void IDBIndex::markAsDeleted()
+{
+ ASSERT(currentThread() == m_objectStore.transaction().database().originThreadID());
+
+ ASSERT(!m_deleted);
+ m_deleted = true;
+}
+
+void IDBIndex::ref()
+{
+ m_objectStore.ref();
}
-IDBDatabaseBackend* IDBIndex::backendDB() const
+void IDBIndex::deref()
{
- return m_transaction->backendDB();
+ m_objectStore.deref();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBIndex.h b/Source/WebCore/Modules/indexeddb/IDBIndex.h
index f954ad6ac..b3134fde2 100644
--- a/Source/WebCore/Modules/indexeddb/IDBIndex.h
+++ b/Source/WebCore/Modules/indexeddb/IDBIndex.h
@@ -1,101 +1,108 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBIndex_h
-#define IDBIndex_h
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
#include "IDBCursor.h"
-#include "IDBDatabase.h"
-#include "IDBDatabaseMetadata.h"
-#include "IDBKeyPath.h"
-#include "IDBKeyRange.h"
-#include "IDBObjectStore.h"
+#include "IDBIndexInfo.h"
#include "IDBRequest.h"
-#include "ScriptWrappable.h"
-#include <wtf/Forward.h>
-#include <wtf/text/WTFString.h>
-#if ENABLE(INDEXED_DATABASE)
+namespace JSC {
+class ExecState;
+}
namespace WebCore {
-class IDBObjectStore;
+class IDBKeyRange;
+
+struct IDBKeyRangeData;
-class IDBIndex : public ScriptWrappable, public RefCounted<IDBIndex> {
+class IDBIndex final : private ActiveDOMObject {
public:
- static PassRefPtr<IDBIndex> create(const IDBIndexMetadata& metadata, IDBObjectStore* objectStore, IDBTransaction* transaction)
- {
- return adoptRef(new IDBIndex(metadata, objectStore, transaction));
- }
- ~IDBIndex();
-
- // Implement the IDL
- const String name() const { return m_metadata.name; }
- PassRefPtr<IDBObjectStore> objectStore() const { return m_objectStore; }
- PassRefPtr<IDBAny> keyPathAny() const { return IDBAny::create(m_metadata.keyPath); }
- const IDBKeyPath keyPath() const { return m_metadata.keyPath; }
- bool unique() const { return m_metadata.unique; }
- bool multiEntry() const { return m_metadata.multiEntry; }
- int64_t id() const { return m_metadata.id; }
-
- // FIXME: Try to modify the code generator so this is unneeded.
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec) { return openCursor(context, keyRange, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) { return openCursor(context, key, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> count(ScriptExecutionContext* context, ExceptionCode& ec) { return count(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
-
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openKeyCursor(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec) { return openKeyCursor(context, keyRange, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) { return openKeyCursor(context, key, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&);
-
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> getKey(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> getKey(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
-
- void markDeleted() { m_deleted = true; }
-
- IDBDatabaseBackend* backendDB() const;
+ IDBIndex(ScriptExecutionContext&, const IDBIndexInfo&, IDBObjectStore&);
+
+ virtual ~IDBIndex();
+
+ const String& name() const;
+ ExceptionOr<void> setName(const String&);
+ IDBObjectStore& objectStore();
+ const IDBKeyPath& keyPath() const;
+ bool unique() const;
+ bool multiEntry() const;
+
+ void rollbackInfoForVersionChangeAbort();
+
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, IDBKeyRange*, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, IDBKeyRange*, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, JSC::JSValue key);
+
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, JSC::JSValue key);
+
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> count);
+
+ const IDBIndexInfo& info() const { return m_info; }
+
+ void markAsDeleted();
+ bool isDeleted() const { return m_deleted; }
+
+ void ref();
+ void deref();
+
+ void* objectStoreAsOpaqueRoot() { return &m_objectStore; }
private:
- IDBIndex(const IDBIndexMetadata&, IDBObjectStore*, IDBTransaction*);
+ ExceptionOr<Ref<IDBRequest>> doCount(JSC::ExecState&, const IDBKeyRangeData&);
+ ExceptionOr<Ref<IDBRequest>> doGet(JSC::ExecState&, const IDBKeyRangeData&);
+ ExceptionOr<Ref<IDBRequest>> doGetKey(JSC::ExecState&, const IDBKeyRangeData&);
+
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
+ bool hasPendingActivity() const final;
+
+ IDBIndexInfo m_info;
+ IDBIndexInfo m_originalInfo;
- IDBIndexMetadata m_metadata;
- RefPtr<IDBObjectStore> m_objectStore;
- RefPtr<IDBTransaction> m_transaction;
- bool m_deleted;
+ bool m_deleted { false };
+
+ // IDBIndex objects are always owned by their referencing IDBObjectStore.
+ // Indexes will never outlive ObjectStores so its okay to keep a raw C++ reference here.
+ IDBObjectStore& m_objectStore;
};
} // namespace WebCore
-#endif
-
-#endif // IDBIndex_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBIndex.idl b/Source/WebCore/Modules/indexeddb/IDBIndex.idl
index 344f516aa..8d7e99dd4 100644
--- a/Source/WebCore/Modules/indexeddb/IDBIndex.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBIndex.idl
@@ -23,28 +23,36 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+// NOTE: This type is specified as 'any' in the IndexedDB specification, but is always
+// constrained to this union.
+typedef (DOMString or sequence<DOMString>) IDBKeyPath;
+
[
Conditional=INDEXED_DATABASE,
- JSNoStaticTables,
- ImplementationLacksVTable,
+ GenerateIsReachable=Impl,
+ JSCustomMarkFunction,
+ SkipVTableValidation,
] interface IDBIndex {
- readonly attribute DOMString name;
+ [SetterMayThrowException] attribute DOMString name;
readonly attribute IDBObjectStore objectStore;
- [ImplementedAs=keyPathAny] readonly attribute IDBAny keyPath;
+ readonly attribute IDBKeyPath? keyPath;
readonly attribute boolean multiEntry;
readonly attribute boolean unique;
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(optional IDBKeyRange? range, optional DOMString direction);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(any key, optional DOMString direction);
+ [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next");
+ [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(any key, optional IDBCursorDirection direction = "next");
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openKeyCursor(optional IDBKeyRange? range, optional DOMString direction);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openKeyCursor(any key, optional DOMString direction);
+ [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next");
+ [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(any key, optional IDBCursorDirection direction = "next");
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(IDBKeyRange? key);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(any key);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest getKey(IDBKeyRange? key);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest getKey(any key);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(optional IDBKeyRange? range);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest get(IDBKeyRange? key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest get(any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getKey(IDBKeyRange? key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getKey(any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAll(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAll(any key, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(any key, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBRequest count(optional IDBKeyRange? range = null);
+ [CallWith=ScriptState, MayThrowException] IDBRequest count(any key);
};
-
diff --git a/Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h b/Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h
deleted file mode 100644
index 724bf093d..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2012 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 GOOGLE 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 IDBIndexMetadata_h
-#define IDBIndexMetadata_h
-
-#include "IDBKeyPath.h"
-#include <wtf/HashMap.h>
-#include <wtf/text/WTFString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-struct IDBIndexMetadata {
- IDBIndexMetadata()
- {
- }
-
- IDBIndexMetadata(const String& name, int64_t id, const IDBKeyPath& keyPath, bool unique, bool multiEntry)
- : name(name)
- , id(id)
- , keyPath(keyPath)
- , unique(unique)
- , multiEntry(multiEntry)
- {
- }
-
- String name;
- int64_t id;
- IDBKeyPath keyPath;
- bool unique;
- bool multiEntry;
-
- IDBIndexMetadata isolatedCopy() const;
-
- static const int64_t InvalidId = -1;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBIndexMetadata_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBKey.cpp b/Source/WebCore/Modules/indexeddb/IDBKey.cpp
index 5196decbd..3e638685c 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKey.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBKey.cpp
@@ -29,21 +29,72 @@
#if ENABLE(INDEXED_DATABASE)
#include "IDBKeyData.h"
+#include <runtime/ArrayBufferView.h>
+#include <runtime/JSArrayBuffer.h>
+#include <runtime/JSArrayBufferView.h>
+#include <runtime/JSCInlines.h>
namespace WebCore {
+using IDBKeyVector = Vector<RefPtr<IDBKey>>;
+
+Ref<IDBKey> IDBKey::createBinary(const ThreadSafeDataBuffer& buffer)
+{
+ return adoptRef(*new IDBKey(buffer));
+}
+
+Ref<IDBKey> IDBKey::createBinary(JSC::JSArrayBuffer& arrayBuffer)
+{
+ auto* buffer = arrayBuffer.impl();
+ return adoptRef(*new IDBKey(ThreadSafeDataBuffer::copyData(buffer->data(), buffer->byteLength())));
+}
+
+Ref<IDBKey> IDBKey::createBinary(JSC::JSArrayBufferView& arrayBufferView)
+{
+ auto bufferView = arrayBufferView.possiblySharedImpl();
+ return adoptRef(*new IDBKey(ThreadSafeDataBuffer::copyData(bufferView->data(), bufferView->byteLength())));
+}
+
+IDBKey::IDBKey(KeyType type, double number)
+ : m_type(type)
+ , m_value(number)
+ , m_sizeEstimate(OverheadSize + sizeof(double))
+{
+}
+
+IDBKey::IDBKey(const String& value)
+ : m_type(KeyType::String)
+ , m_value(value)
+ , m_sizeEstimate(OverheadSize + value.length() * sizeof(UChar))
+{
+}
+
+IDBKey::IDBKey(const IDBKeyVector& keyArray, size_t arraySize)
+ : m_type(KeyType::Array)
+ , m_value(keyArray)
+ , m_sizeEstimate(OverheadSize + arraySize)
+{
+}
+
+IDBKey::IDBKey(const ThreadSafeDataBuffer& buffer)
+ : m_type(KeyType::Binary)
+ , m_value(buffer)
+ , m_sizeEstimate(OverheadSize + buffer.size())
+{
+}
+
IDBKey::~IDBKey()
{
}
bool IDBKey::isValid() const
{
- if (m_type == InvalidType)
+ if (m_type == KeyType::Invalid)
return false;
- if (m_type == ArrayType) {
- for (size_t i = 0; i < m_array.size(); i++) {
- if (!m_array[i]->isValid())
+ if (m_type == KeyType::Array) {
+ for (auto& key : WTF::get<IDBKeyVector>(m_value)) {
+ if (!key->isValid())
return false;
}
}
@@ -51,31 +102,38 @@ bool IDBKey::isValid() const
return true;
}
-int IDBKey::compare(const IDBKey* other) const
+int IDBKey::compare(const IDBKey& other) const
{
- ASSERT(other);
- if (m_type != other->m_type)
- return m_type > other->m_type ? -1 : 1;
+ if (m_type != other.m_type)
+ return m_type > other.m_type ? -1 : 1;
switch (m_type) {
- case ArrayType:
- for (size_t i = 0; i < m_array.size() && i < other->m_array.size(); ++i) {
- if (int result = m_array[i]->compare(other->m_array[i].get()))
+ case KeyType::Array: {
+ auto& array = WTF::get<IDBKeyVector>(m_value);
+ auto& otherArray = WTF::get<IDBKeyVector>(other.m_value);
+ for (size_t i = 0; i < array.size() && i < otherArray.size(); ++i) {
+ if (int result = array[i]->compare(*otherArray[i]))
return result;
}
- if (m_array.size() < other->m_array.size())
+ if (array.size() < otherArray.size())
return -1;
- if (m_array.size() > other->m_array.size())
+ if (array.size() > otherArray.size())
return 1;
return 0;
- case StringType:
- return -codePointCompare(other->m_string, m_string);
- case DateType:
- case NumberType:
- return (m_number < other->m_number) ? -1 :
- (m_number > other-> m_number) ? 1 : 0;
- case InvalidType:
- case MinType:
+ }
+ case KeyType::Binary:
+ return compareBinaryKeyData(WTF::get<ThreadSafeDataBuffer>(m_value), WTF::get<ThreadSafeDataBuffer>(other.m_value));
+ case KeyType::String:
+ return -codePointCompare(WTF::get<String>(other.m_value), WTF::get<String>(m_value));
+ case KeyType::Date:
+ case KeyType::Number: {
+ auto number = WTF::get<double>(m_value);
+ auto otherNumber = WTF::get<double>(other.m_value);
+ return (number < otherNumber) ? -1 : ((number > otherNumber) ? 1 : 0);
+ }
+ case KeyType::Invalid:
+ case KeyType::Min:
+ case KeyType::Max:
ASSERT_NOT_REACHED();
return 0;
}
@@ -84,20 +142,23 @@ int IDBKey::compare(const IDBKey* other) const
return 0;
}
-bool IDBKey::isLessThan(const IDBKey* other) const
+bool IDBKey::isLessThan(const IDBKey& other) const
{
- ASSERT(other);
return compare(other) == -1;
}
-bool IDBKey::isEqual(const IDBKey* other) const
+bool IDBKey::isEqual(const IDBKey& other) const
{
- if (!other)
- return false;
-
return !compare(other);
}
+#if !LOG_DISABLED
+String IDBKey::loggingString() const
+{
+ return IDBKeyData(this).loggingString();
+}
+#endif
+
} // namespace WebCore
#endif
diff --git a/Source/WebCore/Modules/indexeddb/IDBKey.h b/Source/WebCore/Modules/indexeddb/IDBKey.h
index 90b62d599..bc5ed5032 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKey.h
+++ b/Source/WebCore/Modules/indexeddb/IDBKey.h
@@ -23,123 +23,130 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBKey_h
-#define IDBKey_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "IndexedDB.h"
+#include "ThreadSafeDataBuffer.h"
#include <wtf/Forward.h>
#include <wtf/RefCounted.h>
+#include <wtf/Variant.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
+using WebCore::IndexedDB::KeyType;
+
+namespace JSC {
+class JSArrayBuffer;
+class JSArrayBufferView;
+}
+
namespace WebCore {
class IDBKey : public RefCounted<IDBKey> {
public:
- typedef Vector<RefPtr<IDBKey>> KeyArray;
-
- static PassRefPtr<IDBKey> createInvalid()
+ static Ref<IDBKey> createInvalid()
{
- return adoptRef(new IDBKey());
+ return adoptRef(*new IDBKey());
}
- static PassRefPtr<IDBKey> createNumber(double number)
+ static Ref<IDBKey> createNumber(double number)
{
- return adoptRef(new IDBKey(NumberType, number));
+ return adoptRef(*new IDBKey(KeyType::Number, number));
}
- static PassRefPtr<IDBKey> createString(const String& string)
+ static Ref<IDBKey> createString(const String& string)
{
- return adoptRef(new IDBKey(string));
+ return adoptRef(*new IDBKey(string));
}
- static PassRefPtr<IDBKey> createDate(double date)
+ static Ref<IDBKey> createDate(double date)
{
- return adoptRef(new IDBKey(DateType, date));
+ return adoptRef(*new IDBKey(KeyType::Date, date));
}
- static PassRefPtr<IDBKey> createMultiEntryArray(const KeyArray& array)
+ static Ref<IDBKey> createMultiEntryArray(const Vector<RefPtr<IDBKey>>& array)
{
- KeyArray result;
+ Vector<RefPtr<IDBKey>> result;
size_t sizeEstimate = 0;
- for (size_t i = 0; i < array.size(); i++) {
- if (!array[i]->isValid())
+ for (auto& key : array) {
+ if (!key->isValid())
continue;
bool skip = false;
- for (size_t j = 0; j < result.size(); j++) {
- if (array[i]->isEqual(result[j].get())) {
+ for (auto& resultKey : result) {
+ if (key->isEqual(*resultKey)) {
skip = true;
break;
}
}
if (!skip) {
- result.append(array[i]);
- sizeEstimate += array[i]->m_sizeEstimate;
+ result.append(key);
+ sizeEstimate += key->m_sizeEstimate;
}
}
- RefPtr<IDBKey> idbKey = adoptRef(new IDBKey(result, sizeEstimate));
+ Ref<IDBKey> idbKey = adoptRef(*new IDBKey(result, sizeEstimate));
ASSERT(idbKey->isValid());
- return idbKey.release();
+ return idbKey;
}
- static PassRefPtr<IDBKey> createArray(const KeyArray& array)
+ static Ref<IDBKey> createArray(const Vector<RefPtr<IDBKey>>& array)
{
size_t sizeEstimate = 0;
- for (size_t i = 0; i < array.size(); ++i)
- sizeEstimate += array[i]->m_sizeEstimate;
+ for (auto& key : array)
+ sizeEstimate += key->m_sizeEstimate;
- return adoptRef(new IDBKey(array, sizeEstimate));
+ return adoptRef(*new IDBKey(array, sizeEstimate));
}
- ~IDBKey();
+ static Ref<IDBKey> createBinary(const ThreadSafeDataBuffer&);
+ static Ref<IDBKey> createBinary(JSC::JSArrayBuffer&);
+ static Ref<IDBKey> createBinary(JSC::JSArrayBufferView&);
- // In order of the least to the highest precedent in terms of sort order.
- enum Type {
- InvalidType = 0,
- ArrayType,
- StringType,
- DateType,
- NumberType,
- MinType
- };
+ WEBCORE_EXPORT ~IDBKey();
- Type type() const { return m_type; }
- bool isValid() const;
+ KeyType type() const { return m_type; }
+ WEBCORE_EXPORT bool isValid() const;
- const KeyArray& array() const
+ const Vector<RefPtr<IDBKey>>& array() const
{
- ASSERT(m_type == ArrayType);
- return m_array;
+ ASSERT(m_type == KeyType::Array);
+ return WTF::get<Vector<RefPtr<IDBKey>>>(m_value);
}
const String& string() const
{
- ASSERT(m_type == StringType);
- return m_string;
+ ASSERT(m_type == KeyType::String);
+ return WTF::get<String>(m_value);
}
double date() const
{
- ASSERT(m_type == DateType);
- return m_number;
+ ASSERT(m_type == KeyType::Date);
+ return WTF::get<double>(m_value);
}
double number() const
{
- ASSERT(m_type == NumberType);
- return m_number;
+ ASSERT(m_type == KeyType::Number);
+ return WTF::get<double>(m_value);
+ }
+
+ const ThreadSafeDataBuffer& binary() const
+ {
+ ASSERT(m_type == KeyType::Binary);
+ return WTF::get<ThreadSafeDataBuffer>(m_value);
}
- int compare(const IDBKey* other) const;
- bool isLessThan(const IDBKey* other) const;
- bool isEqual(const IDBKey* other) const;
+ int compare(const IDBKey& other) const;
+ bool isLessThan(const IDBKey& other) const;
+ bool isEqual(const IDBKey& other) const;
size_t sizeEstimate() const { return m_sizeEstimate; }
- static int compareTypes(Type a, Type b)
+ static int compareTypes(KeyType a, KeyType b)
{
return b - a;
}
@@ -147,16 +154,24 @@ public:
using RefCounted<IDBKey>::ref;
using RefCounted<IDBKey>::deref;
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
private:
- IDBKey() : m_type(InvalidType), m_number(0), m_sizeEstimate(OverheadSize) { }
- IDBKey(Type type, double number) : m_type(type), m_number(number), m_sizeEstimate(OverheadSize + sizeof(double)) { }
- explicit IDBKey(const String& value) : m_type(StringType), m_string(value), m_number(0), m_sizeEstimate(OverheadSize + value.length() * sizeof(UChar)) { }
- IDBKey(const KeyArray& keyArray, size_t arraySize) : m_type(ArrayType), m_array(keyArray), m_number(0), m_sizeEstimate(OverheadSize + arraySize) { }
+ IDBKey()
+ : m_type(KeyType::Invalid)
+ , m_sizeEstimate(OverheadSize)
+ {
+ }
+
+ IDBKey(KeyType, double number);
+ explicit IDBKey(const String& value);
+ IDBKey(const Vector<RefPtr<IDBKey>>& keyArray, size_t arraySize);
+ explicit IDBKey(const ThreadSafeDataBuffer&);
- const Type m_type;
- const KeyArray m_array;
- const String m_string;
- const double m_number;
+ const KeyType m_type;
+ Variant<Vector<RefPtr<IDBKey>>, String, double, ThreadSafeDataBuffer> m_value;
const size_t m_sizeEstimate;
@@ -164,8 +179,43 @@ private:
enum { OverheadSize = 16 };
};
+inline int compareBinaryKeyData(const Vector<uint8_t>& a, const Vector<uint8_t>& b)
+{
+ size_t length = std::min(a.size(), b.size());
+
+ for (size_t i = 0; i < length; ++i) {
+ if (a[i] > b[i])
+ return 1;
+ if (a[i] < b[i])
+ return -1;
+ }
+
+ if (a.size() == b.size())
+ return 0;
+
+ if (a.size() > b.size())
+ return 1;
+
+ return -1;
}
-#endif // ENABLE(INDEXED_DATABASE)
+inline int compareBinaryKeyData(const ThreadSafeDataBuffer& a, const ThreadSafeDataBuffer& b)
+{
+ auto* aData = a.data();
+ auto* bData = b.data();
+
+ // Covers the cases where both pointers are null as well as both pointing to the same buffer.
+ if (aData == bData)
+ return 0;
-#endif // IDBKey_h
+ if (aData && !bData)
+ return 1;
+ if (!aData && bData)
+ return -1;
+
+ return compareBinaryKeyData(*aData, *bData);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp
index 46ca3fc0c..3d5abf383 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyData.cpp
@@ -29,68 +29,74 @@
#if ENABLE(INDEXED_DATABASE)
#include "KeyedCoding.h"
+#include <wtf/text/StringBuilder.h>
namespace WebCore {
IDBKeyData::IDBKeyData(const IDBKey* key)
- : type(IDBKey::InvalidType)
- , numberValue(0)
- , isNull(false)
+ : m_type(KeyType::Invalid)
{
if (!key) {
- isNull = true;
+ m_isNull = true;
return;
}
- type = key->type();
- numberValue = 0;
+ m_type = key->type();
- switch (type) {
- case IDBKey::InvalidType:
+ switch (m_type) {
+ case KeyType::Invalid:
break;
- case IDBKey::ArrayType:
- for (auto key2 : key->array())
- arrayValue.append(IDBKeyData(key2.get()));
+ case KeyType::Array: {
+ m_value = Vector<IDBKeyData>();
+ auto& array = WTF::get<Vector<IDBKeyData>>(m_value);
+ for (auto& key2 : key->array())
+ array.append(IDBKeyData(key2.get()));
break;
- case IDBKey::StringType:
- stringValue = key->string();
+ }
+ case KeyType::Binary:
+ m_value = key->binary();
break;
- case IDBKey::DateType:
- numberValue = key->date();
+ case KeyType::String:
+ m_value = key->string();
break;
- case IDBKey::NumberType:
- numberValue = key->number();
+ case KeyType::Date:
+ m_value = key->date();
break;
- case IDBKey::MinType:
- ASSERT_NOT_REACHED();
+ case KeyType::Number:
+ m_value = key->number();
+ break;
+ case KeyType::Max:
+ case KeyType::Min:
break;
}
}
-PassRefPtr<IDBKey> IDBKeyData::maybeCreateIDBKey() const
+RefPtr<IDBKey> IDBKeyData::maybeCreateIDBKey() const
{
- if (isNull)
+ if (m_isNull)
return nullptr;
- switch (type) {
- case IDBKey::InvalidType:
+ switch (m_type) {
+ case KeyType::Invalid:
return IDBKey::createInvalid();
- case IDBKey::ArrayType:
- {
- IDBKey::KeyArray array;
- for (auto keyData : arrayValue) {
- array.append(keyData.maybeCreateIDBKey());
- ASSERT(array.last());
- }
- return IDBKey::createArray(array);
+ case KeyType::Array: {
+ Vector<RefPtr<IDBKey>> array;
+ for (auto& keyData : WTF::get<Vector<IDBKeyData>>(m_value)) {
+ array.append(keyData.maybeCreateIDBKey());
+ ASSERT(array.last());
}
- case IDBKey::StringType:
- return IDBKey::createString(stringValue);
- case IDBKey::DateType:
- return IDBKey::createDate(numberValue);
- case IDBKey::NumberType:
- return IDBKey::createNumber(numberValue);
- case IDBKey::MinType:
+ return IDBKey::createArray(array);
+ }
+ case KeyType::Binary:
+ return IDBKey::createBinary(WTF::get<ThreadSafeDataBuffer>(m_value));
+ case KeyType::String:
+ return IDBKey::createString(WTF::get<String>(m_value));
+ case KeyType::Date:
+ return IDBKey::createDate(WTF::get<double>(m_value));
+ case KeyType::Number:
+ return IDBKey::createNumber(WTF::get<double>(m_value));
+ case KeyType::Max:
+ case KeyType::Min:
ASSERT_NOT_REACHED();
return nullptr;
}
@@ -99,74 +105,354 @@ PassRefPtr<IDBKey> IDBKeyData::maybeCreateIDBKey() const
return nullptr;
}
+IDBKeyData::IDBKeyData(const IDBKeyData& that, IsolatedCopyTag)
+{
+ isolatedCopy(that, *this);
+}
+
IDBKeyData IDBKeyData::isolatedCopy() const
{
- IDBKeyData result;
- result.type = type;
- result.isNull = isNull;
-
- switch (type) {
- case IDBKey::InvalidType:
- return result;
- case IDBKey::ArrayType:
- for (auto key : arrayValue)
- result.arrayValue.append(key.isolatedCopy());
- return result;
- case IDBKey::StringType:
- result.stringValue = stringValue.isolatedCopy();
- return result;
- case IDBKey::DateType:
- case IDBKey::NumberType:
- result.numberValue = numberValue;
- return result;
- case IDBKey::MinType:
- ASSERT_NOT_REACHED();
- return result;
+ return { *this, IsolatedCopy };
+}
+
+void IDBKeyData::isolatedCopy(const IDBKeyData& source, IDBKeyData& destination)
+{
+ destination.m_type = source.m_type;
+ destination.m_isNull = source.m_isNull;
+
+ switch (source.m_type) {
+ case KeyType::Invalid:
+ return;
+ case KeyType::Array: {
+ destination.m_value = Vector<IDBKeyData>();
+ auto& destinationArray = WTF::get<Vector<IDBKeyData>>(destination.m_value);
+ for (auto& key : WTF::get<Vector<IDBKeyData>>(source.m_value))
+ destinationArray.append(key.isolatedCopy());
+ return;
+ }
+ case KeyType::Binary:
+ destination.m_value = WTF::get<ThreadSafeDataBuffer>(source.m_value);
+ return;
+ case KeyType::String:
+ destination.m_value = WTF::get<String>(source.m_value).isolatedCopy();
+ return;
+ case KeyType::Date:
+ case KeyType::Number:
+ destination.m_value = WTF::get<double>(source.m_value);
+ return;
+ case KeyType::Max:
+ case KeyType::Min:
+ return;
}
ASSERT_NOT_REACHED();
- return result;
}
void IDBKeyData::encode(KeyedEncoder& encoder) const
{
- encoder.encodeBool("null", isNull);
- if (isNull)
+ encoder.encodeBool("null", m_isNull);
+ if (m_isNull)
return;
- encoder.encodeEnum("type", type);
+ encoder.encodeEnum("type", m_type);
- switch (type) {
- case IDBKey::InvalidType:
+ switch (m_type) {
+ case KeyType::Invalid:
return;
- case IDBKey::ArrayType:
- encoder.encodeObjects("array", arrayValue.begin(), arrayValue.end(), [](KeyedEncoder& encoder, const IDBKeyData& key) {
- encoder.encodeObject("idbKeyData", key, [](KeyedEncoder& encoder, const IDBKeyData& key) {
- key.encode(encoder);
- });
+ case KeyType::Array: {
+ auto& array = WTF::get<Vector<IDBKeyData>>(m_value);
+ encoder.encodeObjects("array", array.begin(), array.end(), [](KeyedEncoder& encoder, const IDBKeyData& key) {
+ key.encode(encoder);
});
return;
- case IDBKey::StringType:
- encoder.encodeString("string", stringValue);
+ }
+ case KeyType::Binary: {
+ auto* data = WTF::get<ThreadSafeDataBuffer>(m_value).data();
+ encoder.encodeBool("hasBinary", !!data);
+ if (data)
+ encoder.encodeBytes("binary", data->data(), data->size());
+ return;
+ }
+ case KeyType::String:
+ encoder.encodeString("string", WTF::get<String>(m_value));
return;
- case IDBKey::DateType:
- case IDBKey::NumberType:
- encoder.encodeDouble("number", numberValue);
+ case KeyType::Date:
+ case KeyType::Number:
+ encoder.encodeDouble("number", WTF::get<double>(m_value));
return;
- case IDBKey::MinType:
- ASSERT_NOT_REACHED();
+ case KeyType::Max:
+ case KeyType::Min:
return;
}
ASSERT_NOT_REACHED();
}
-bool IDBKeyData::decode(KeyedDecoder&, IDBKeyData&)
+bool IDBKeyData::decode(KeyedDecoder& decoder, IDBKeyData& result)
+{
+ if (!decoder.decodeBool("null", result.m_isNull))
+ return false;
+
+ if (result.m_isNull)
+ return true;
+
+ auto enumFunction = [](int64_t value) {
+ return value == KeyType::Max
+ || value == KeyType::Invalid
+ || value == KeyType::Array
+ || value == KeyType::Binary
+ || value == KeyType::String
+ || value == KeyType::Date
+ || value == KeyType::Number
+ || value == KeyType::Min;
+ };
+ if (!decoder.decodeEnum("type", result.m_type, enumFunction))
+ return false;
+
+ if (result.m_type == KeyType::Invalid)
+ return true;
+
+ if (result.m_type == KeyType::Max)
+ return true;
+
+ if (result.m_type == KeyType::Min)
+ return true;
+
+ if (result.m_type == KeyType::String) {
+ result.m_value = String();
+ return decoder.decodeString("string", WTF::get<String>(result.m_value));
+ }
+
+ if (result.m_type == KeyType::Number || result.m_type == KeyType::Date) {
+ result.m_value = 0.0;
+ return decoder.decodeDouble("number", WTF::get<double>(result.m_value));
+ }
+
+ if (result.m_type == KeyType::Binary) {
+ result.m_value = ThreadSafeDataBuffer();
+
+ bool hasBinaryData;
+ if (!decoder.decodeBool("hasBinary", hasBinaryData))
+ return false;
+
+ if (!hasBinaryData)
+ return true;
+
+ Vector<uint8_t> bytes;
+ if (!decoder.decodeBytes("binary", bytes))
+ return false;
+
+ result.m_value = ThreadSafeDataBuffer::adoptVector(bytes);
+ return true;
+ }
+
+ ASSERT(result.m_type == KeyType::Array);
+
+ auto arrayFunction = [](KeyedDecoder& decoder, IDBKeyData& result) {
+ return decode(decoder, result);
+ };
+
+ result.m_value = Vector<IDBKeyData>();
+ return decoder.decodeObjects("array", WTF::get<Vector<IDBKeyData>>(result.m_value), arrayFunction);
+}
+
+int IDBKeyData::compare(const IDBKeyData& other) const
+{
+ if (m_type == KeyType::Invalid) {
+ if (other.m_type != KeyType::Invalid)
+ return -1;
+ if (other.m_type == KeyType::Invalid)
+ return 0;
+ } else if (other.m_type == KeyType::Invalid)
+ return 1;
+
+ // The IDBKey::m_type enum is in reverse sort order.
+ if (m_type != other.m_type)
+ return m_type < other.m_type ? 1 : -1;
+
+ // The types are the same, so handle actual value comparison.
+ switch (m_type) {
+ case KeyType::Invalid:
+ // Invalid type should have been fully handled above
+ ASSERT_NOT_REACHED();
+ return 0;
+ case KeyType::Array: {
+ auto& array = WTF::get<Vector<IDBKeyData>>(m_value);
+ auto& otherArray = WTF::get<Vector<IDBKeyData>>(other.m_value);
+ for (size_t i = 0; i < array.size() && i < otherArray.size(); ++i) {
+ if (int result = array[i].compare(otherArray[i]))
+ return result;
+ }
+ if (array.size() < otherArray.size())
+ return -1;
+ if (array.size() > otherArray.size())
+ return 1;
+ return 0;
+ }
+ case KeyType::Binary:
+ return compareBinaryKeyData(WTF::get<ThreadSafeDataBuffer>(m_value), WTF::get<ThreadSafeDataBuffer>(other.m_value));
+ case KeyType::String:
+ return codePointCompare(WTF::get<String>(m_value), WTF::get<String>(other.m_value));
+ case KeyType::Date:
+ case KeyType::Number: {
+ auto number = WTF::get<double>(m_value);
+ auto otherNumber = WTF::get<double>(other.m_value);
+
+ if (number == otherNumber)
+ return 0;
+ return number > otherNumber ? 1 : -1;
+ }
+ case KeyType::Max:
+ case KeyType::Min:
+ return 0;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+#if !LOG_DISABLED
+String IDBKeyData::loggingString() const
{
- // FIXME: Implement when IDB Get support is implemented (<rdar://problem/15779644>)
- return false;
+ if (m_isNull)
+ return "<null>";
+
+ String result;
+
+ switch (m_type) {
+ case KeyType::Invalid:
+ return "<invalid>";
+ case KeyType::Array: {
+ StringBuilder builder;
+ builder.appendLiteral("<array> - { ");
+ auto& array = WTF::get<Vector<IDBKeyData>>(m_value);
+ for (size_t i = 0; i < array.size(); ++i) {
+ builder.append(array[i].loggingString());
+ if (i < array.size() - 1)
+ builder.appendLiteral(", ");
+ }
+ builder.appendLiteral(" }");
+ result = builder.toString();
+ break;
+ }
+ case KeyType::Binary: {
+ StringBuilder builder;
+ builder.append("<binary> - ");
+
+ auto* data = WTF::get<ThreadSafeDataBuffer>(m_value).data();
+ if (!data) {
+ builder.append("(null)");
+ result = builder.toString();
+ break;
+ }
+
+ size_t i = 0;
+ for (; i < 8 && i < data->size(); ++i)
+ builder.append(String::format("%02x", data->at(i)));
+
+ if (data->size() > 8)
+ builder.append("...");
+
+ result = builder.toString();
+ break;
+ }
+ case KeyType::String:
+ result = "<string> - " + WTF::get<String>(m_value);
+ break;
+ case KeyType::Date:
+ return String::format("<date> - %f", WTF::get<double>(m_value));
+ case KeyType::Number:
+ return String::format("<number> - %f", WTF::get<double>(m_value));
+ case KeyType::Max:
+ return "<maximum>";
+ case KeyType::Min:
+ return "<minimum>";
+ }
+
+ if (result.length() > 150) {
+ result.truncate(147);
+ result.append(WTF::ASCIILiteral("..."));
+ }
+
+ return result;
}
+#endif
+void IDBKeyData::setArrayValue(const Vector<IDBKeyData>& value)
+{
+ *this = IDBKeyData();
+ m_value = value;
+ m_type = KeyType::Array;
+ m_isNull = false;
}
+void IDBKeyData::setBinaryValue(const ThreadSafeDataBuffer& value)
+{
+ *this = IDBKeyData();
+ m_value = value;
+ m_type = KeyType::Binary;
+ m_isNull = false;
+}
+
+void IDBKeyData::setStringValue(const String& value)
+{
+ *this = IDBKeyData();
+ m_value = value;
+ m_type = KeyType::String;
+ m_isNull = false;
+}
+
+void IDBKeyData::setDateValue(double value)
+{
+ *this = IDBKeyData();
+ m_value = value;
+ m_type = KeyType::Date;
+ m_isNull = false;
+}
+
+void IDBKeyData::setNumberValue(double value)
+{
+ *this = IDBKeyData();
+ m_value = value;
+ m_type = KeyType::Number;
+ m_isNull = false;
+}
+
+IDBKeyData IDBKeyData::deletedValue()
+{
+ IDBKeyData result;
+ result.m_isNull = false;
+ result.m_isDeletedValue = true;
+ return result;
+}
+
+bool IDBKeyData::operator<(const IDBKeyData& rhs) const
+{
+ return compare(rhs) < 0;
+}
+
+bool IDBKeyData::operator==(const IDBKeyData& other) const
+{
+ if (m_type != other.m_type || m_isNull != other.m_isNull || m_isDeletedValue != other.m_isDeletedValue)
+ return false;
+ switch (m_type) {
+ case KeyType::Invalid:
+ case KeyType::Max:
+ case KeyType::Min:
+ return true;
+ case KeyType::Number:
+ case KeyType::Date:
+ return WTF::get<double>(m_value) == WTF::get<double>(other.m_value);
+ case KeyType::String:
+ return WTF::get<String>(m_value) == WTF::get<String>(other.m_value);
+ case KeyType::Binary:
+ return WTF::get<ThreadSafeDataBuffer>(m_value) == WTF::get<ThreadSafeDataBuffer>(other.m_value);
+ case KeyType::Array:
+ return WTF::get<Vector<IDBKeyData>>(m_value) == WTF::get<Vector<IDBKeyData>>(other.m_value);
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WebCore
+
#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyData.h b/Source/WebCore/Modules/indexeddb/IDBKeyData.h
index 6a4f64a06..98f0b7359 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyData.h
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyData.h
@@ -23,43 +23,282 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBKeyData_h
-#define IDBKeyData_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
#include "IDBKey.h"
+#include <wtf/Variant.h>
+#include <wtf/text/StringHash.h>
namespace WebCore {
class KeyedDecoder;
class KeyedEncoder;
-struct IDBKeyData {
+class IDBKeyData {
+public:
IDBKeyData()
- : type(IDBKey::InvalidType)
- , numberValue(0)
- , isNull(true)
+ : m_type(KeyType::Invalid)
+ , m_isNull(true)
{
}
- IDBKeyData(const IDBKey*);
+ WEBCORE_EXPORT IDBKeyData(const IDBKey*);
- PassRefPtr<IDBKey> maybeCreateIDBKey() const;
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBKeyData(const IDBKeyData&, IsolatedCopyTag);
+
+ static IDBKeyData minimum()
+ {
+ IDBKeyData result;
+ result.m_type = KeyType::Min;
+ result.m_isNull = false;
+ return result;
+ }
+
+ static IDBKeyData maximum()
+ {
+ IDBKeyData result;
+ result.m_type = KeyType::Max;
+ result.m_isNull = false;
+ return result;
+ }
+
+ WEBCORE_EXPORT RefPtr<IDBKey> maybeCreateIDBKey() const;
IDBKeyData isolatedCopy() const;
- void encode(KeyedEncoder&) const;
- static bool decode(KeyedDecoder&, IDBKeyData&);
+ WEBCORE_EXPORT void encode(KeyedEncoder&) const;
+ WEBCORE_EXPORT static bool decode(KeyedDecoder&, IDBKeyData&);
+
+ // compare() has the same semantics as strcmp().
+ // - Returns negative if this IDBKeyData is less than other.
+ // - Returns positive if this IDBKeyData is greater than other.
+ // - Returns zero if this IDBKeyData is equal to other.
+ WEBCORE_EXPORT int compare(const IDBKeyData& other) const;
+
+ void setArrayValue(const Vector<IDBKeyData>&);
+ void setBinaryValue(const ThreadSafeDataBuffer&);
+ void setStringValue(const String&);
+ void setDateValue(double);
+ WEBCORE_EXPORT void setNumberValue(double);
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBKeyData&);
+
+#if !LOG_DISABLED
+ WEBCORE_EXPORT String loggingString() const;
+#endif
+
+ bool isNull() const { return m_isNull; }
+ bool isValid() const { return m_type != KeyType::Invalid; }
+ KeyType type() const { return m_type; }
+
+ bool operator<(const IDBKeyData&) const;
+ bool operator>(const IDBKeyData& other) const
+ {
+ return !(*this < other) && !(*this == other);
+ }
+
+ bool operator<=(const IDBKeyData& other) const
+ {
+ return !(*this > other);
+ }
+
+ bool operator>=(const IDBKeyData& other) const
+ {
+ return !(*this < other);
+ }
+
+ bool operator==(const IDBKeyData& other) const;
+ bool operator!=(const IDBKeyData& other) const
+ {
+ return !(*this == other);
+ }
+
+ unsigned hash() const
+ {
+ Vector<unsigned> hashCodes;
+ hashCodes.append(static_cast<unsigned>(m_type));
+ hashCodes.append(m_isNull ? 1 : 0);
+ hashCodes.append(m_isDeletedValue ? 1 : 0);
+ switch (m_type) {
+ case KeyType::Invalid:
+ case KeyType::Max:
+ case KeyType::Min:
+ break;
+ case KeyType::Number:
+ case KeyType::Date:
+ hashCodes.append(StringHasher::hashMemory<sizeof(double)>(&WTF::get<double>(m_value)));
+ break;
+ case KeyType::String:
+ hashCodes.append(StringHash::hash(WTF::get<String>(m_value)));
+ break;
+ case KeyType::Binary: {
+ auto* data = WTF::get<ThreadSafeDataBuffer>(m_value).data();
+ if (!data)
+ hashCodes.append(0);
+ else
+ hashCodes.append(StringHasher::hashMemory(data->data(), data->size()));
+ break;
+ }
+ case KeyType::Array:
+ for (auto& key : WTF::get<Vector<IDBKeyData>>(m_value))
+ hashCodes.append(key.hash());
+ break;
+ }
+
+ return StringHasher::hashMemory(hashCodes.data(), hashCodes.size() * sizeof(unsigned));
+ }
- IDBKey::Type type;
- Vector<IDBKeyData> arrayValue;
- String stringValue;
- double numberValue;
- bool isNull;
+ static IDBKeyData deletedValue();
+ bool isDeletedValue() const { return m_isDeletedValue; }
+
+ String string() const
+ {
+ ASSERT(m_type == KeyType::String);
+ return WTF::get<String>(m_value);
+ }
+
+ double date() const
+ {
+ ASSERT(m_type == KeyType::Date);
+ return WTF::get<double>(m_value);
+ }
+
+ double number() const
+ {
+ ASSERT(m_type == KeyType::Number);
+ return WTF::get<double>(m_value);
+ }
+
+ const ThreadSafeDataBuffer& binary() const
+ {
+ ASSERT(m_type == KeyType::Binary);
+ return WTF::get<ThreadSafeDataBuffer>(m_value);
+ }
+
+ const Vector<IDBKeyData>& array() const
+ {
+ ASSERT(m_type == KeyType::Array);
+ return WTF::get<Vector<IDBKeyData>>(m_value);
+ }
+
+private:
+ static void isolatedCopy(const IDBKeyData& source, IDBKeyData& destination);
+
+ KeyType m_type;
+ Variant<Vector<IDBKeyData>, String, double, ThreadSafeDataBuffer> m_value;
+
+ bool m_isNull { false };
+ bool m_isDeletedValue { false };
};
+struct IDBKeyDataHash {
+ static unsigned hash(const IDBKeyData& a) { return a.hash(); }
+ static bool equal(const IDBKeyData& a, const IDBKeyData& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+struct IDBKeyDataHashTraits : public WTF::CustomHashTraits<IDBKeyData> {
+ static const bool emptyValueIsZero = false;
+ static const bool hasIsEmptyValueFunction = true;
+
+ static void constructDeletedValue(IDBKeyData& key)
+ {
+ key = IDBKeyData::deletedValue();
+ }
+
+ static bool isDeletedValue(const IDBKeyData& key)
+ {
+ return key.isDeletedValue();
+ }
+
+ static IDBKeyData emptyValue()
+ {
+ return IDBKeyData();
+ }
+
+ static bool isEmptyValue(const IDBKeyData& key)
+ {
+ return key.isNull();
+ }
+};
+
+template<class Encoder>
+void IDBKeyData::encode(Encoder& encoder) const
+{
+ encoder << m_isNull;
+ if (m_isNull)
+ return;
+
+ encoder.encodeEnum(m_type);
+
+ switch (m_type) {
+ case KeyType::Invalid:
+ case KeyType::Max:
+ case KeyType::Min:
+ break;
+ case KeyType::Array:
+ encoder << WTF::get<Vector<IDBKeyData>>(m_value);
+ break;
+ case KeyType::Binary:
+ encoder << WTF::get<ThreadSafeDataBuffer>(m_value);
+ break;
+ case KeyType::String:
+ encoder << WTF::get<String>(m_value);
+ break;
+ case KeyType::Date:
+ case KeyType::Number:
+ encoder << WTF::get<double>(m_value);
+ break;
+ }
+}
+
+template<class Decoder>
+bool IDBKeyData::decode(Decoder& decoder, IDBKeyData& keyData)
+{
+ if (!decoder.decode(keyData.m_isNull))
+ return false;
+
+ if (keyData.m_isNull)
+ return true;
+
+ if (!decoder.decodeEnum(keyData.m_type))
+ return false;
+
+ switch (keyData.m_type) {
+ case KeyType::Invalid:
+ case KeyType::Max:
+ case KeyType::Min:
+ break;
+ case KeyType::Array:
+ keyData.m_value = Vector<IDBKeyData>();
+ if (!decoder.decode(WTF::get<Vector<IDBKeyData>>(keyData.m_value)))
+ return false;
+ break;
+ case KeyType::Binary:
+ keyData.m_value = ThreadSafeDataBuffer();
+ if (!decoder.decode(WTF::get<ThreadSafeDataBuffer>(keyData.m_value)))
+ return false;
+ break;
+ case KeyType::String:
+ keyData.m_value = String();
+ if (!decoder.decode(WTF::get<String>(keyData.m_value)))
+ return false;
+ break;
+ case KeyType::Date:
+ case KeyType::Number:
+ keyData.m_value = 0.0;
+ if (!decoder.decode(WTF::get<double>(keyData.m_value)))
+ return false;
+ break;
+ }
+
+ return true;
+}
+
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBKeyData_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp
index 2b46ff81d..e76d4f9ce 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -24,14 +25,13 @@
*/
#include "config.h"
-#include "IDBKeyPath.h"
#if ENABLE(INDEXED_DATABASE)
+#include "IDBKeyPath.h"
-#include "KeyedCoding.h"
#include <wtf/ASCIICType.h>
#include <wtf/dtoa.h>
-#include <wtf/unicode/Unicode.h>
+#include <wtf/text/StringBuilder.h>
namespace WebCore {
@@ -46,8 +46,7 @@ public:
explicit IDBKeyPathLexer(const String& s)
: m_string(s)
- , m_ptr(s.deprecatedCharacters())
- , m_end(s.deprecatedCharacters() + s.length())
+ , m_remainingText(s)
, m_currentTokenType(TokenError)
{
}
@@ -65,23 +64,23 @@ public:
private:
TokenType lex(String&);
TokenType lexIdentifier(String&);
+
String m_currentElement;
- String m_string;
- const UChar* m_ptr;
- const UChar* m_end;
+ const String m_string;
+ StringView m_remainingText;
TokenType m_currentTokenType;
};
IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(String& element)
{
- if (m_ptr >= m_end)
+ if (m_remainingText.isEmpty())
return TokenEnd;
- ASSERT_WITH_SECURITY_IMPLICATION(m_ptr < m_end);
- if (*m_ptr == '.') {
- ++m_ptr;
+ if (m_remainingText[0] == '.') {
+ m_remainingText = m_remainingText.substring(1);
return TokenDot;
}
+
return lexIdentifier(element);
}
@@ -109,16 +108,16 @@ static inline bool isIdentifierCharacter(UChar c)
IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(String& element)
{
- const UChar* start = m_ptr;
- if (m_ptr < m_end && isIdentifierStartCharacter(*m_ptr))
- ++m_ptr;
+ StringView start = m_remainingText;
+ if (!m_remainingText.isEmpty() && isIdentifierStartCharacter(m_remainingText[0]))
+ m_remainingText = m_remainingText.substring(1);
else
return TokenError;
- while (m_ptr < m_end && isIdentifierCharacter(*m_ptr))
- ++m_ptr;
+ while (!m_remainingText.isEmpty() && isIdentifierCharacter(m_remainingText[0]))
+ m_remainingText = m_remainingText.substring(1);
- element = String(start, m_ptr - start);
+ element = start.substring(0, start.length() - m_remainingText.length()).toString();
return TokenIdentifier;
}
@@ -127,7 +126,7 @@ static bool IDBIsValidKeyPath(const String& keyPath)
IDBKeyPathParseError error;
Vector<String> keyPathElements;
IDBParseKeyPath(keyPath, keyPathElements, error);
- return error == IDBKeyPathParseErrorNone;
+ return error == IDBKeyPathParseError::None;
}
void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPathParseError& error)
@@ -148,7 +147,7 @@ void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPath
else if (tokenType == IDBKeyPathLexer::TokenEnd)
state = End;
else {
- error = IDBKeyPathParseErrorStart;
+ error = IDBKeyPathParseError::Start;
return;
}
@@ -167,7 +166,7 @@ void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPath
else if (tokenType == IDBKeyPathLexer::TokenEnd)
state = End;
else {
- error = IDBKeyPathParseErrorIdentifier;
+ error = IDBKeyPathParseError::Identifier;
return;
}
break;
@@ -180,130 +179,74 @@ void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPath
if (tokenType == IDBKeyPathLexer::TokenIdentifier)
state = Identifier;
else {
- error = IDBKeyPathParseErrorDot;
+ error = IDBKeyPathParseError::Dot;
return;
}
break;
}
case End: {
- error = IDBKeyPathParseErrorNone;
+ error = IDBKeyPathParseError::None;
return;
}
}
}
}
-IDBKeyPath::IDBKeyPath(const String& string)
- : m_type(StringType)
- , m_string(string)
-{
- ASSERT(!m_string.isNull());
-}
-
-IDBKeyPath::IDBKeyPath(const Vector<String>& array)
- : m_type(ArrayType)
- , m_array(array)
+bool isIDBKeyPathValid(const IDBKeyPath& keyPath)
{
-#ifndef NDEBUG
- for (size_t i = 0; i < m_array.size(); ++i)
- ASSERT(!m_array[i].isNull());
-#endif
-}
-
-bool IDBKeyPath::isValid() const
-{
- switch (m_type) {
- case NullType:
- return false;
-
- case StringType:
- return IDBIsValidKeyPath(m_string);
-
- case ArrayType:
- if (m_array.isEmpty())
+ auto visitor = WTF::makeVisitor([](const String& string) {
+ return IDBIsValidKeyPath(string);
+ }, [](const Vector<String>& vector) {
+ if (vector.isEmpty())
return false;
- for (size_t i = 0; i < m_array.size(); ++i) {
- if (!IDBIsValidKeyPath(m_array[i]))
+ for (auto& key : vector) {
+ if (!IDBIsValidKeyPath(key))
return false;
}
return true;
- }
- ASSERT_NOT_REACHED();
- return false;
+ });
+ return WTF::visit(visitor, keyPath);
}
-bool IDBKeyPath::operator==(const IDBKeyPath& other) const
+IDBKeyPath isolatedCopy(const IDBKeyPath& keyPath)
{
- if (m_type != other.m_type)
- return false;
-
- switch (m_type) {
- case NullType:
- return true;
- case StringType:
- return m_string == other.m_string;
- case ArrayType:
- return m_array == other.m_array;
- }
- ASSERT_NOT_REACHED();
- return false;
+ auto visitor = WTF::makeVisitor([](const String& string) -> IDBKeyPath {
+ return string.isolatedCopy();
+ }, [](const Vector<String>& vector) -> IDBKeyPath {
+ Vector<String> vectorCopy;
+ vectorCopy.reserveInitialCapacity(vector.size());
+ for (auto& string : vector)
+ vectorCopy.uncheckedAppend(string.isolatedCopy());
+ return vectorCopy;
+ });
+
+ return WTF::visit(visitor, keyPath);
}
-IDBKeyPath IDBKeyPath::isolatedCopy() const
-{
- IDBKeyPath result;
- result.m_type = m_type;
- result.m_string = m_string.isolatedCopy();
-
- result.m_array.reserveInitialCapacity(m_array.size());
- for (size_t i = 0; i < m_array.size(); ++i)
- result.m_array.uncheckedAppend(m_array[i].isolatedCopy());
-
- return result;
-}
-
-void IDBKeyPath::encode(KeyedEncoder& encoder) const
-{
- encoder.encodeEnum("type", m_type);
- switch (m_type) {
- case IDBKeyPath::NullType:
- break;
- case IDBKeyPath::StringType:
- encoder.encodeString("string", m_string);
- break;
- case IDBKeyPath::ArrayType:
- encoder.encodeObjects("array", m_array.begin(), m_array.end(), [](WebCore::KeyedEncoder& encoder, const String& string) {
- encoder.encodeString("string", string);
- });
- break;
- default:
- ASSERT_NOT_REACHED();
- };
-}
-
-bool IDBKeyPath::decode(KeyedDecoder& decoder, IDBKeyPath& result)
+#ifndef NDEBUG
+String loggingString(const IDBKeyPath& path)
{
- auto enumFunction = [](int64_t value) {
- return value == NullType || value == StringType || value == ArrayType;
- };
-
- if (!decoder.decodeVerifiedEnum("type", result.m_type, enumFunction))
- return false;
-
- if (result.m_type == NullType)
- return true;
-
- if (result.m_type == StringType)
- return decoder.decodeString("string", result.m_string);
-
- ASSERT(result.m_type == ArrayType);
+ auto visitor = WTF::makeVisitor([](const String& string) {
+ return makeString("< ", string, " >");
+ }, [](const Vector<String>& strings) {
+ if (strings.isEmpty())
+ return String("< >");
+
+ StringBuilder builder;
+ builder.append("< ");
+ for (size_t i = 0; i < strings.size() - 1; ++i) {
+ builder.append(strings[i]);
+ builder.append(", ");
+ }
+ builder.append(strings.last());
+ builder.append(" >");
- auto arrayFunction = [](KeyedDecoder& decoder, String& result) {
- return decoder.decodeString("string", result);
- };
+ return builder.toString();
+ });
- return decoder.decodeObjects("array", result.m_array, arrayFunction);
+ return WTF::visit(visitor, path);
}
+#endif
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyPath.h b/Source/WebCore/Modules/indexeddb/IDBKeyPath.h
index 6dcd7deac..c5a04b3d2 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyPath.h
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyPath.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,71 +24,39 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBKeyPath_h
-#define IDBKeyPath_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include <wtf/Variant.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-class KeyedDecoder;
-class KeyedEncoder;
+using IDBKeyPath = WTF::Variant<String, Vector<String>>;
+bool isIDBKeyPathValid(const IDBKeyPath&);
-enum IDBKeyPathParseError {
- IDBKeyPathParseErrorNone,
- IDBKeyPathParseErrorStart,
- IDBKeyPathParseErrorIdentifier,
- IDBKeyPathParseErrorDot,
+enum class IDBKeyPathParseError {
+ None,
+ Start,
+ Identifier,
+ Dot,
};
void IDBParseKeyPath(const String&, Vector<String>&, IDBKeyPathParseError&);
-
-class IDBKeyPath {
-public:
- IDBKeyPath() : m_type(NullType) { }
- explicit IDBKeyPath(const String&);
- explicit IDBKeyPath(const Vector<String>& array);
-
- enum Type {
- NullType = 0,
- StringType,
- ArrayType
- };
-
- Type type() const { return m_type; }
-
- const Vector<String>& array() const
- {
- ASSERT(m_type == ArrayType);
- return m_array;
- }
-
- const String& string() const
- {
- ASSERT(m_type == StringType);
- return m_string;
- }
-
- bool isNull() const { return m_type == NullType; }
- bool isValid() const;
- bool operator==(const IDBKeyPath& other) const;
-
- IDBKeyPath isolatedCopy() const;
-
- void encode(KeyedEncoder&) const;
- static bool decode(KeyedDecoder&, IDBKeyPath&);
-
-private:
- Type m_type;
- String m_string;
- Vector<String> m_array;
-};
+IDBKeyPath isolatedCopy(const IDBKeyPath&);
+inline std::optional<IDBKeyPath> isolatedCopy(const std::optional<IDBKeyPath>& variant)
+{
+ if (!variant)
+ return { };
+ return isolatedCopy(variant.value());
+}
+
+#ifndef NDEBUG
+String loggingString(const IDBKeyPath&);
+#endif
} // namespace WebCore
#endif
-
-#endif // IDBKeyPath_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp
index 3d54fc2b2..c6511acc8 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp
@@ -26,118 +26,118 @@
#include "config.h"
#include "IDBKeyRange.h"
-#include "DOMRequestState.h"
+#if ENABLE(INDEXED_DATABASE)
+
#include "IDBBindingUtilities.h"
#include "IDBDatabaseException.h"
#include "IDBKey.h"
+#include "IDBKeyData.h"
+#include "ScriptExecutionContext.h"
+#include <runtime/JSCJSValue.h>
-#if ENABLE(INDEXED_DATABASE)
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBKeyRange> IDBKeyRange::create(PassRefPtr<IDBKey> prpKey)
+Ref<IDBKeyRange> IDBKeyRange::create(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& upper, bool isLowerOpen, bool isUpperOpen)
{
- RefPtr<IDBKey> key = prpKey;
- return adoptRef(new IDBKeyRange(key, key, LowerBoundClosed, UpperBoundClosed));
+ return adoptRef(*new IDBKeyRange(WTFMove(lower), WTFMove(upper), isLowerOpen, isUpperOpen));
}
-IDBKeyRange::IDBKeyRange(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, LowerBoundType lowerType, UpperBoundType upperType)
- : m_lower(lower)
- , m_upper(upper)
- , m_lowerType(lowerType)
- , m_upperType(upperType)
+Ref<IDBKeyRange> IDBKeyRange::create(RefPtr<IDBKey>&& key)
{
+ auto upper = key;
+ return create(WTFMove(key), WTFMove(upper), false, false);
}
-Deprecated::ScriptValue IDBKeyRange::lowerValue(ScriptExecutionContext* context) const
+IDBKeyRange::IDBKeyRange(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& upper, bool isLowerOpen, bool isUpperOpen)
+ : m_lower(WTFMove(lower))
+ , m_upper(WTFMove(upper))
+ , m_isLowerOpen(isLowerOpen)
+ , m_isUpperOpen(isUpperOpen)
{
- DOMRequestState requestState(context);
- return idbKeyToScriptValue(&requestState, m_lower);
}
-Deprecated::ScriptValue IDBKeyRange::upperValue(ScriptExecutionContext* context) const
+IDBKeyRange::~IDBKeyRange()
{
- DOMRequestState requestState(context);
- return idbKeyToScriptValue(&requestState, m_upper);
}
-PassRefPtr<IDBKeyRange> IDBKeyRange::only(PassRefPtr<IDBKey> prpKey, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::only(RefPtr<IDBKey>&& key)
{
- RefPtr<IDBKey> key = prpKey;
- if (!key || !key->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ if (!key || !key->isValid())
+ return Exception { IDBDatabaseException::DataError };
- return IDBKeyRange::create(key, key, LowerBoundClosed, UpperBoundClosed);
+ return create(WTFMove(key));
}
-PassRefPtr<IDBKeyRange> IDBKeyRange::only(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::only(ExecState& state, JSValue keyValue)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
- if (!key || !key->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
-
- return IDBKeyRange::create(key, key, LowerBoundClosed, UpperBoundClosed);
+ return only(scriptValueToIDBKey(state, keyValue));
}
-PassRefPtr<IDBKeyRange> IDBKeyRange::lowerBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& boundValue, bool open, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::lowerBound(ExecState& state, JSValue boundValue, bool open)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> bound = scriptValueToIDBKey(&requestState, boundValue);
- if (!bound || !bound->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ auto bound = scriptValueToIDBKey(state, boundValue);
+ if (!bound->isValid())
+ return Exception { IDBDatabaseException::DataError };
- return IDBKeyRange::create(bound, 0, open ? LowerBoundOpen : LowerBoundClosed, UpperBoundOpen);
+ return create(WTFMove(bound), nullptr, open, true);
}
-PassRefPtr<IDBKeyRange> IDBKeyRange::upperBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& boundValue, bool open, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::upperBound(ExecState& state, JSValue boundValue, bool open)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> bound = scriptValueToIDBKey(&requestState, boundValue);
- if (!bound || !bound->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ auto bound = scriptValueToIDBKey(state, boundValue);
+ if (!bound->isValid())
+ return Exception { IDBDatabaseException::DataError };
- return IDBKeyRange::create(0, bound, LowerBoundOpen, open ? UpperBoundOpen : UpperBoundClosed);
+ return create(nullptr, WTFMove(bound), true, open);
}
-PassRefPtr<IDBKeyRange> IDBKeyRange::bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lowerValue, const Deprecated::ScriptValue& upperValue, bool lowerOpen, bool upperOpen, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::bound(ExecState& state, JSValue lowerValue, JSValue upperValue, bool lowerOpen, bool upperOpen)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> lower = scriptValueToIDBKey(&requestState, lowerValue);
- RefPtr<IDBKey> upper = scriptValueToIDBKey(&requestState, upperValue);
+ auto lower = scriptValueToIDBKey(state, lowerValue);
+ auto upper = scriptValueToIDBKey(state, upperValue);
- if (!lower || !lower->isValid() || !upper || !upper->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- if (upper->isLessThan(lower.get())) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- if (upper->isEqual(lower.get()) && (lowerOpen || upperOpen)) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ if (!lower->isValid() || !upper->isValid())
+ return Exception { IDBDatabaseException::DataError };
+ if (upper->isLessThan(lower.get()))
+ return Exception { IDBDatabaseException::DataError };
+ if (upper->isEqual(lower.get()) && (lowerOpen || upperOpen))
+ return Exception { IDBDatabaseException::DataError };
- return IDBKeyRange::create(lower, upper, lowerOpen ? LowerBoundOpen : LowerBoundClosed, upperOpen ? UpperBoundOpen : UpperBoundClosed);
+ return create(WTFMove(lower), WTFMove(upper), lowerOpen, upperOpen);
}
bool IDBKeyRange::isOnlyKey() const
{
- if (m_lowerType != LowerBoundClosed || m_upperType != UpperBoundClosed)
- return false;
+ return m_lower && m_upper && !m_isLowerOpen && !m_isUpperOpen && m_lower->isEqual(*m_upper);
+}
+
+ExceptionOr<bool> IDBKeyRange::includes(JSC::ExecState& state, JSC::JSValue keyValue)
+{
+ auto key = scriptValueToIDBKey(state, keyValue);
+ if (!key->isValid())
+ return Exception { IDBDatabaseException::DataError, "Failed to execute 'includes' on 'IDBKeyRange': The passed-in value is not a valid IndexedDB key." };
+
+ if (m_lower) {
+ int compare = m_lower->compare(key.get());
+
+ if (compare > 0)
+ return false;
+ if (m_isLowerOpen && !compare)
+ return false;
+ }
+
+ if (m_upper) {
+ int compare = m_upper->compare(key.get());
+
+ if (compare < 0)
+ return false;
+ if (m_isUpperOpen && !compare)
+ return false;
+ }
- ASSERT(m_lower);
- ASSERT(m_upper);
- return m_lower->isEqual(m_upper.get());
+ return true;
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRange.h b/Source/WebCore/Modules/indexeddb/IDBKeyRange.h
index e551cf4d3..0f13f97b0 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyRange.h
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyRange.h
@@ -23,73 +23,57 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBKeyRange_h
-#define IDBKeyRange_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
-#include "Dictionary.h"
-#include "IDBKey.h"
+#include "ExceptionOr.h"
#include "ScriptWrappable.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace JSC {
+class ExecState;
+class JSValue;
+}
namespace WebCore {
-typedef int ExceptionCode;
+class IDBKey;
+class ScriptExecutionContext;
class IDBKeyRange : public ScriptWrappable, public RefCounted<IDBKeyRange> {
public:
- enum LowerBoundType {
- LowerBoundOpen,
- LowerBoundClosed
- };
- enum UpperBoundType {
- UpperBoundOpen,
- UpperBoundClosed
- };
-
- static PassRefPtr<IDBKeyRange> create(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, LowerBoundType lowerType, UpperBoundType upperType)
- {
- return adoptRef(new IDBKeyRange(lower, upper, lowerType, upperType));
- }
- static PassRefPtr<IDBKeyRange> create(PassRefPtr<IDBKey> prpKey);
- ~IDBKeyRange() { }
+ static Ref<IDBKeyRange> create(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& upper, bool isLowerOpen, bool isUpperOpen);
+ static Ref<IDBKeyRange> create(RefPtr<IDBKey>&&);
+ ~IDBKeyRange();
- PassRefPtr<IDBKey> lower() const { return m_lower; }
- PassRefPtr<IDBKey> upper() const { return m_upper; }
+ IDBKey* lower() const { return m_lower.get(); }
+ IDBKey* upper() const { return m_upper.get(); }
+ bool lowerOpen() const { return m_isLowerOpen; }
+ bool upperOpen() const { return m_isUpperOpen; }
- Deprecated::ScriptValue lowerValue(ScriptExecutionContext*) const;
- Deprecated::ScriptValue upperValue(ScriptExecutionContext*) const;
- bool lowerOpen() const { return m_lowerType == LowerBoundOpen; }
- bool upperOpen() const { return m_upperType == UpperBoundOpen; }
+ static ExceptionOr<Ref<IDBKeyRange>> only(RefPtr<IDBKey>&& value);
+ static ExceptionOr<Ref<IDBKeyRange>> only(JSC::ExecState&, JSC::JSValue key);
- static PassRefPtr<IDBKeyRange> only(PassRefPtr<IDBKey> value, ExceptionCode&);
- static PassRefPtr<IDBKeyRange> only(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
+ static ExceptionOr<Ref<IDBKeyRange>> lowerBound(JSC::ExecState&, JSC::JSValue bound, bool open);
+ static ExceptionOr<Ref<IDBKeyRange>> upperBound(JSC::ExecState&, JSC::JSValue bound, bool open);
- static PassRefPtr<IDBKeyRange> lowerBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& bound, ExceptionCode& ec) { return lowerBound(context, bound, false, ec); }
- static PassRefPtr<IDBKeyRange> lowerBound(ScriptExecutionContext*, const Deprecated::ScriptValue& bound, bool open, ExceptionCode&);
+ static ExceptionOr<Ref<IDBKeyRange>> bound(JSC::ExecState&, JSC::JSValue lower, JSC::JSValue upper, bool lowerOpen, bool upperOpen);
- static PassRefPtr<IDBKeyRange> upperBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& bound, ExceptionCode& ec) { return upperBound(context, bound, false, ec); }
- static PassRefPtr<IDBKeyRange> upperBound(ScriptExecutionContext*, const Deprecated::ScriptValue& bound, bool open, ExceptionCode&);
+ ExceptionOr<bool> includes(JSC::ExecState&, JSC::JSValue key);
- static PassRefPtr<IDBKeyRange> bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lower, const Deprecated::ScriptValue& upper, ExceptionCode& ec) { return bound(context, lower, upper, false, false, ec); }
- static PassRefPtr<IDBKeyRange> bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lower, const Deprecated::ScriptValue& upper, bool lowerOpen, ExceptionCode& ec) { return bound(context, lower, upper, lowerOpen, false, ec); }
- static PassRefPtr<IDBKeyRange> bound(ScriptExecutionContext*, const Deprecated::ScriptValue& lower, const Deprecated::ScriptValue& upper, bool lowerOpen, bool upperOpen, ExceptionCode&);
-
- bool isOnlyKey() const;
+ WEBCORE_EXPORT bool isOnlyKey() const;
private:
- IDBKeyRange(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, LowerBoundType lowerType, UpperBoundType upperType);
+ IDBKeyRange(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& upper, bool isLowerOpen, bool isUpperOpen);
RefPtr<IDBKey> m_lower;
RefPtr<IDBKey> m_upper;
- LowerBoundType m_lowerType;
- UpperBoundType m_upperType;
+ bool m_isLowerOpen;
+ bool m_isUpperOpen;
};
} // namespace WebCore
#endif
-
-#endif // IDBKeyRange_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl b/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl
index c8367f749..8927ec9f7 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyRange.idl
@@ -25,17 +25,17 @@
[
Conditional=INDEXED_DATABASE,
- JSNoStaticTables,
ImplementationLacksVTable,
] interface IDBKeyRange {
- [ImplementedAs=lowerValue,CallWith=ScriptExecutionContext] readonly attribute any lower;
- [ImplementedAs=upperValue,CallWith=ScriptExecutionContext] readonly attribute any upper;
+ [OverrideIDLType=IDLIDBKey] readonly attribute any lower;
+ [OverrideIDLType=IDLIDBKey] readonly attribute any upper;
readonly attribute boolean lowerOpen;
readonly attribute boolean upperOpen;
- [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange only(any value);
- [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange lowerBound(any lower, optional boolean open);
- [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange upperBound(any upper, optional boolean open);
- [CallWith=ScriptExecutionContext, RaisesException] static IDBKeyRange bound(any lower, any upper, optional boolean lowerOpen, optional boolean upperOpen);
-};
+ [CallWith=ScriptState, MayThrowException] static IDBKeyRange only(any value);
+ [CallWith=ScriptState, MayThrowException] static IDBKeyRange lowerBound(any lower, optional boolean open = false);
+ [CallWith=ScriptState, MayThrowException] static IDBKeyRange upperBound(any upper, optional boolean open = false);
+ [CallWith=ScriptState, MayThrowException] static IDBKeyRange bound(any lower, any upper, optional boolean lowerOpen = false, optional boolean upperOpen = false);
+ [CallWith=ScriptState, MayThrowException] boolean includes(any key);
+};
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp
index af1fb296b..7132edb81 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp
@@ -28,8 +28,28 @@
#if ENABLE(INDEXED_DATABASE)
+#include "IDBKey.h"
+
namespace WebCore {
+IDBKeyRangeData::IDBKeyRangeData(IDBKey* key)
+ : isNull(!key)
+ , lowerKey(key)
+ , upperKey(key)
+ , lowerOpen(false)
+ , upperOpen(false)
+{
+}
+
+IDBKeyRangeData::IDBKeyRangeData(const IDBKeyData& keyData)
+ : isNull(keyData.isNull())
+ , lowerKey(keyData)
+ , upperKey(keyData)
+ , lowerOpen(false)
+ , upperOpen(false)
+{
+}
+
IDBKeyRangeData IDBKeyRangeData::isolatedCopy() const
{
IDBKeyRangeData result;
@@ -43,13 +63,68 @@ IDBKeyRangeData IDBKeyRangeData::isolatedCopy() const
return result;
}
-PassRefPtr<IDBKeyRange> IDBKeyRangeData::maybeCreateIDBKeyRange() const
+RefPtr<IDBKeyRange> IDBKeyRangeData::maybeCreateIDBKeyRange() const
{
if (isNull)
return nullptr;
- return IDBKeyRange::create(lowerKey.maybeCreateIDBKey(), upperKey.maybeCreateIDBKey(), lowerOpen ? IDBKeyRange::LowerBoundOpen : IDBKeyRange::LowerBoundClosed, upperOpen ? IDBKeyRange::UpperBoundOpen : IDBKeyRange::UpperBoundClosed);
+ return IDBKeyRange::create(lowerKey.maybeCreateIDBKey(), upperKey.maybeCreateIDBKey(), lowerOpen, upperOpen);
+}
+
+bool IDBKeyRangeData::isExactlyOneKey() const
+{
+ if (isNull || lowerOpen || upperOpen || !upperKey.isValid() || !lowerKey.isValid())
+ return false;
+
+ return !lowerKey.compare(upperKey);
+}
+
+bool IDBKeyRangeData::containsKey(const IDBKeyData& key) const
+{
+ if (lowerKey.isValid()) {
+ auto compare = lowerKey.compare(key);
+ if (compare > 0)
+ return false;
+ if (lowerOpen && !compare)
+ return false;
+ }
+ if (upperKey.isValid()) {
+ auto compare = upperKey.compare(key);
+ if (compare < 0)
+ return false;
+ if (upperOpen && !compare)
+ return false;
+ }
+
+ return true;
+}
+
+bool IDBKeyRangeData::isValid() const
+{
+ if (isNull)
+ return false;
+
+ if (!lowerKey.isValid() && !lowerKey.isNull())
+ return false;
+
+ if (!upperKey.isValid() && !upperKey.isNull())
+ return false;
+
+ return true;
+}
+
+#if !LOG_DISABLED
+String IDBKeyRangeData::loggingString() const
+{
+ auto result = makeString(lowerOpen ? "( " : "[ ", lowerKey.loggingString(), ", ", upperKey.loggingString(), upperOpen ? " )" : " ]");
+ if (result.length() > 400) {
+ result.truncate(397);
+ result.append(WTF::ASCIILiteral("..."));
+ }
+
+ return result;
}
+#endif
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h
index d35a54393..12ecd4de3 100644
--- a/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h
+++ b/Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h
@@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBKeyRangeData_h
-#define IDBKeyRangeData_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
@@ -33,6 +32,8 @@
namespace WebCore {
+class IDBKey;
+
struct IDBKeyRangeData {
IDBKeyRangeData()
: isNull(true)
@@ -41,6 +42,18 @@ struct IDBKeyRangeData {
{
}
+ static IDBKeyRangeData allKeys()
+ {
+ IDBKeyRangeData result;
+ result.isNull = false;
+ result.lowerKey = IDBKeyData::minimum();
+ result.upperKey = IDBKeyData::maximum();
+ return result;
+ }
+
+ IDBKeyRangeData(IDBKey*);
+ IDBKeyRangeData(const IDBKeyData&);
+
IDBKeyRangeData(IDBKeyRange* keyRange)
: isNull(!keyRange)
, lowerOpen(false)
@@ -49,15 +62,22 @@ struct IDBKeyRangeData {
if (isNull)
return;
- lowerKey = keyRange->lower().get();
- upperKey = keyRange->upper().get();
+ lowerKey = keyRange->lower();
+ upperKey = keyRange->upper();
lowerOpen = keyRange->lowerOpen();
upperOpen = keyRange->upperOpen();
}
IDBKeyRangeData isolatedCopy() const;
- PassRefPtr<IDBKeyRange> maybeCreateIDBKeyRange() const;
+ WEBCORE_EXPORT RefPtr<IDBKeyRange> maybeCreateIDBKeyRange() const;
+
+ WEBCORE_EXPORT bool isExactlyOneKey() const;
+ bool containsKey(const IDBKeyData&) const;
+ bool isValid() const;
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBKeyRangeData&);
bool isNull;
@@ -66,9 +86,46 @@ struct IDBKeyRangeData {
bool lowerOpen;
bool upperOpen;
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
};
+template<class Encoder>
+void IDBKeyRangeData::encode(Encoder& encoder) const
+{
+ encoder << isNull;
+ if (isNull)
+ return;
+
+ encoder << upperKey << lowerKey << upperOpen << lowerOpen;
+}
+
+template<class Decoder>
+bool IDBKeyRangeData::decode(Decoder& decoder, IDBKeyRangeData& keyRange)
+{
+ if (!decoder.decode(keyRange.isNull))
+ return false;
+
+ if (keyRange.isNull)
+ return true;
+
+ if (!decoder.decode(keyRange.upperKey))
+ return false;
+
+ if (!decoder.decode(keyRange.lowerKey))
+ return false;
+
+ if (!decoder.decode(keyRange.upperOpen))
+ return false;
+
+ if (!decoder.decode(keyRange.lowerOpen))
+ return false;
+
+ return true;
+}
+
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBKeyRangeData_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp b/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp
index 8d735ad6f..592c67d3b 100644
--- a/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -29,565 +29,674 @@
#if ENABLE(INDEXED_DATABASE)
#include "DOMStringList.h"
-#include "IDBAny.h"
+#include "Document.h"
+#include "ExceptionCode.h"
#include "IDBBindingUtilities.h"
-#include "IDBCursorWithValue.h"
+#include "IDBCursor.h"
#include "IDBDatabase.h"
#include "IDBDatabaseException.h"
+#include "IDBError.h"
+#include "IDBGetRecordData.h"
#include "IDBIndex.h"
#include "IDBKey.h"
-#include "IDBKeyPath.h"
-#include "IDBKeyRange.h"
+#include "IDBKeyRangeData.h"
+#include "IDBRequest.h"
#include "IDBTransaction.h"
+#include "IndexedDB.h"
#include "Logging.h"
+#include "Page.h"
#include "ScriptExecutionContext.h"
+#include "ScriptState.h"
#include "SerializedScriptValue.h"
-#include "SharedBuffer.h"
+#include <heap/HeapInlines.h>
+#include <wtf/Locker.h>
+
+using namespace JSC;
namespace WebCore {
-IDBObjectStore::IDBObjectStore(const IDBObjectStoreMetadata& metadata, IDBTransaction* transaction)
- : m_metadata(metadata)
+IDBObjectStore::IDBObjectStore(ScriptExecutionContext& context, const IDBObjectStoreInfo& info, IDBTransaction& transaction)
+ : ActiveDOMObject(&context)
+ , m_info(info)
+ , m_originalInfo(info)
, m_transaction(transaction)
- , m_deleted(false)
{
- ASSERT(m_transaction);
- // We pass a reference to this object before it can be adopted.
- relaxAdoptionRequirement();
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ suspendIfNeeded();
}
-PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const
+IDBObjectStore::~IDBObjectStore()
{
- LOG(StorageAPI, "IDBObjectStore::indexNames");
- RefPtr<DOMStringList> indexNames = DOMStringList::create();
- for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it)
- indexNames->append(it->value.name);
- indexNames->sort();
- return indexNames.release();
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
}
-PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+const char* IDBObjectStore::activeDOMObjectName() const
{
- LOG(StorageAPI, "IDBObjectStore::get");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!keyRange) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->get(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, keyRange, false, request);
- return request.release();
+ return "IDBObjectStore";
+}
+
+bool IDBObjectStore::canSuspendForDocumentSuspension() const
+{
+ return false;
+}
+
+bool IDBObjectStore::hasPendingActivity() const
+{
+ return !m_transaction.isFinished();
}
-PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+const String& IDBObjectStore::name() const
{
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return get(context, keyRange.release(), ec);
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+ return m_info.name();
}
-static void generateIndexKeysForValue(DOMRequestState* requestState, const IDBIndexMetadata& indexMetadata, const Deprecated::ScriptValue& objectValue, IDBObjectStore::IndexKeys* indexKeys)
+ExceptionOr<void> IDBObjectStore::setName(const String& name)
{
- ASSERT(indexKeys);
- RefPtr<IDBKey> indexKey = createIDBKeyFromScriptValueAndKeyPath(requestState, objectValue, indexMetadata.keyPath);
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
- if (!indexKey)
- return;
+ if (m_deleted)
+ return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBObjectStore': The object store has been deleted.") };
- if (!indexMetadata.multiEntry || indexKey->type() != IDBKey::ArrayType) {
- if (!indexKey->isValid())
- return;
+ if (!m_transaction.isVersionChange())
+ return Exception { INVALID_STATE_ERR, ASCIILiteral("Failed set property 'name' on 'IDBObjectStore': The object store's transaction is not a version change transaction.") };
- indexKeys->append(indexKey);
- } else {
- ASSERT(indexMetadata.multiEntry);
- ASSERT(indexKey->type() == IDBKey::ArrayType);
- indexKey = IDBKey::createMultiEntryArray(indexKey->array());
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed set property 'name' on 'IDBObjectStore': The object store's transaction is not active.") };
+
+ if (m_info.name() == name)
+ return { };
+
+ if (m_transaction.database().info().hasObjectStore(name))
+ return Exception { IDBDatabaseException::ConstraintError, makeString("Failed set property 'name' on 'IDBObjectStore': The database already has an object store named '", name, "'.") };
+
+ m_transaction.database().renameObjectStore(*this, name);
+ m_info.rename(name);
+
+ return { };
+}
+
+const std::optional<IDBKeyPath>& IDBObjectStore::keyPath() const
+{
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+ return m_info.keyPath();
+}
- for (size_t i = 0; i < indexKey->array().size(); ++i)
- indexKeys->append(indexKey->array()[i]);
+RefPtr<DOMStringList> IDBObjectStore::indexNames() const
+{
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ RefPtr<DOMStringList> indexNames = DOMStringList::create();
+
+ if (!m_deleted) {
+ for (auto& name : m_info.indexNames())
+ indexNames->append(name);
+ indexNames->sort();
}
+
+ return indexNames;
}
-PassRefPtr<IDBRequest> IDBObjectStore::add(JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+IDBTransaction& IDBObjectStore::transaction()
{
- LOG(StorageAPI, "IDBObjectStore::add");
- return put(IDBDatabaseBackend::AddOnly, IDBAny::create(this), state, value, key, ec);
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+ return m_transaction;
}
-PassRefPtr<IDBRequest> IDBObjectStore::add(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec)
+bool IDBObjectStore::autoIncrement() const
{
- LOG(StorageAPI, "IDBObjectStore::add");
- return put(IDBDatabaseBackend::AddOnly, IDBAny::create(this), state, value, static_cast<IDBKey*>(0), ec);
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+ return m_info.autoIncrement();
}
-PassRefPtr<IDBRequest> IDBObjectStore::put(JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, RefPtr<IDBKeyRange> range, IDBCursorDirection direction)
{
- LOG(StorageAPI, "IDBObjectStore::put");
- return put(IDBDatabaseBackend::AddOrUpdate, IDBAny::create(this), state, value, key, ec);
+ LOG(IndexedDB, "IDBObjectStore::openCursor");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The transaction is inactive or finished.") };
+
+ auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), range.get(), direction, IndexedDB::CursorType::KeyAndValue);
+ return m_transaction.requestOpenCursor(execState, *this, info);
}
-PassRefPtr<IDBRequest> IDBObjectStore::put(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
{
- LOG(StorageAPI, "IDBObjectStore::put");
- return put(IDBDatabaseBackend::AddOrUpdate, IDBAny::create(this), state, value, static_cast<IDBKey*>(0), ec);
+ auto onlyResult = IDBKeyRange::only(execState, key);
+ if (onlyResult.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The parameter is not a valid key.") };
+
+ return openCursor(execState, onlyResult.releaseReturnValue(), direction);
}
-PassRefPtr<IDBRequest> IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBAny> source, JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, RefPtr<IDBKeyRange> range, IDBCursorDirection direction)
{
- ScriptExecutionContext* context = scriptExecutionContextFromExecState(state);
- DOMRequestState requestState(context);
- RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
- return put(putMode, source, state, value, key.release(), ec);
+ LOG(IndexedDB, "IDBObjectStore::openCursor");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBObjectStore': The transaction is inactive or finished.") };
+
+ auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), range.get(), direction, IndexedDB::CursorType::KeyOnly);
+ return m_transaction.requestOpenCursor(execState, *this, info);
}
-PassRefPtr<IDBRequest> IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBAny> source, JSC::ExecState* state, Deprecated::ScriptValue& value, PassRefPtr<IDBKey> prpKey, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
{
- RefPtr<IDBKey> key = prpKey;
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (m_transaction->isReadOnly()) {
- ec = IDBDatabaseException::ReadOnlyError;
- return 0;
- }
+ auto onlyResult = IDBKeyRange::only(execState, key);
+ if (onlyResult.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBObjectStore': The parameter is not a valid key or key range.") };
- // FIXME: Expose the JS engine exception state through ScriptState.
- bool didThrow = false;
- RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::serialize(value, state, nullptr, nullptr, didThrow);
- if (didThrow) {
- // Setting an explicit ExceptionCode here would defer handling the already thrown exception.
- return 0;
- }
+ return openKeyCursor(execState, onlyResult.releaseReturnValue(), direction);
+}
- if (serializedValue->hasBlobURLs()) {
- // FIXME: Add Blob/File/FileList support
- ec = IDBDatabaseException::DataCloneError;
- return 0;
- }
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::get(ExecState& execState, JSValue key)
+{
+ LOG(IndexedDB, "IDBObjectStore::get");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
- const IDBKeyPath& keyPath = m_metadata.keyPath;
- const bool usesInLineKeys = !keyPath.isNull();
- const bool hasKeyGenerator = autoIncrement();
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted.") };
- ScriptExecutionContext* context = scriptExecutionContextFromExecState(state);
- DOMRequestState requestState(context);
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished.") };
- if (putMode != IDBDatabaseBackend::CursorUpdate && usesInLineKeys && key) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- if (!usesInLineKeys && !hasKeyGenerator && !key) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- if (usesInLineKeys) {
- RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(&requestState, value, keyPath);
- if (keyPathKey && !keyPathKey->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- if (!hasKeyGenerator && !keyPathKey) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- if (hasKeyGenerator && !keyPathKey) {
- if (!canInjectIDBKeyIntoScriptValue(&requestState, value, keyPath)) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
- }
- if (keyPathKey)
- key = keyPathKey;
- }
- if (key && !key->isValid()) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ auto idbKey = scriptValueToIDBKey(execState, key);
+ if (!idbKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The parameter is not a valid key.") };
- Vector<int64_t> indexIds;
- Vector<IndexKeys> indexKeys;
- for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
- IndexKeys keys;
- generateIndexKeysForValue(&requestState, it->value, value, &keys);
- indexIds.append(it->key);
- indexKeys.append(keys);
- }
+ return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyAndValue });
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::get(ExecState& execState, IDBKeyRange* keyRange)
+{
+ LOG(IndexedDB, "IDBObjectStore::get");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError };
- RefPtr<IDBRequest> request = IDBRequest::create(context, source, m_transaction.get());
- Vector<uint8_t> valueBytes = serializedValue->toWireBytes();
- // This is a hack to account for disagreements about whether SerializedScriptValue should deal in Vector<uint8_t> or Vector<char>.
- // See https://lists.webkit.org/pipermail/webkit-dev/2013-February/023682.html
- Vector<char>* valueBytesSigned = reinterpret_cast<Vector<char>*>(&valueBytes);
- RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(*valueBytesSigned);
- backendDB()->put(m_transaction->id(), id(), valueBuffer, key.release(), static_cast<IDBDatabaseBackend::PutMode>(putMode), request, indexIds, indexKeys);
- return request.release();
+ IDBKeyRangeData keyRangeData(keyRange);
+ if (!keyRangeData.isValid())
+ return Exception { IDBDatabaseException::DataError };
+
+ return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyAndValue });
}
-PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getKey(ExecState& execState, JSValue key)
{
- LOG(StorageAPI, "IDBObjectStore::delete");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (m_transaction->isReadOnly()) {
- ec = IDBDatabaseException::ReadOnlyError;
- return 0;
- }
- if (!keyRange) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ LOG(IndexedDB, "IDBObjectStore::getKey");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted.") };
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->deleteRange(m_transaction->id(), id(), keyRange, request);
- return request.release();
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished.") };
+
+ auto idbKey = scriptValueToIDBKey(execState, key);
+ if (!idbKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key.") };
+
+ return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyOnly });
}
-PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getKey(ExecState& execState, IDBKeyRange* keyRange)
{
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return deleteFunction(context, keyRange.release(), ec);
+ LOG(IndexedDB, "IDBObjectStore::getKey");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished.") };
+
+ IDBKeyRangeData keyRangeData(keyRange);
+ if (!keyRangeData.isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key range.") };
+
+ return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyOnly });
}
-PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::add(ExecState& execState, JSValue value, JSValue key)
{
- LOG(StorageAPI, "IDBObjectStore::clear");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (m_transaction->isReadOnly()) {
- ec = IDBDatabaseException::ReadOnlyError;
- return 0;
- }
+ RefPtr<IDBKey> idbKey;
+ if (!key.isUndefined())
+ idbKey = scriptValueToIDBKey(execState, key);
+ return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, InlineKeyCheck::Perform);
+}
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->clearObjectStore(m_transaction->id(), id(), request);
- return request.release();
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::put(ExecState& execState, JSValue value, JSValue key)
+{
+ RefPtr<IDBKey> idbKey;
+ if (!key.isUndefined())
+ idbKey = scriptValueToIDBKey(execState, key);
+ return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::Overwrite, InlineKeyCheck::Perform);
}
-namespace {
-// This class creates the index keys for a given index by extracting
-// them from the SerializedScriptValue, for all the existing values in
-// the objectStore. It only needs to be kept alive by virtue of being
-// a listener on an IDBRequest object, in the same way that JavaScript
-// cursor success handlers are kept alive.
-class IndexPopulator : public EventListener {
-public:
- static PassRefPtr<IndexPopulator> create(PassRefPtr<IDBDatabaseBackend> backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- {
- return adoptRef(new IndexPopulator(backend, transactionId, objectStoreId, indexMetadata));
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putForCursorUpdate(ExecState& state, JSValue value, JSValue key)
+{
+ return putOrAdd(state, value, scriptValueToIDBKey(state, key), IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor, InlineKeyCheck::DoNotPerform);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putOrAdd(ExecState& state, JSValue value, RefPtr<IDBKey> key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, InlineKeyCheck inlineKeyCheck)
+{
+ VM& vm = state.vm();
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+
+ LOG(IndexedDB, "IDBObjectStore::putOrAdd");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ auto context = scriptExecutionContextFromExecState(&state);
+ if (!context)
+ return Exception { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to store record in object store because it does not have a valid script execution context") };
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to store record in an IDBObjectStore: The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is inactive or finished.") };
+
+ if (m_transaction.isReadOnly())
+ return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is read-only.") };
+
+ auto serializedValue = SerializedScriptValue::create(state, value);
+ if (UNLIKELY(scope.exception())) {
+ // Clear the DOM exception from the serializer so we can give a more targeted exception.
+ scope.clearException();
+
+ return Exception { IDBDatabaseException::DataCloneError, ASCIILiteral("Failed to store record in an IDBObjectStore: An object could not be cloned.") };
}
- virtual bool operator==(const EventListener& other)
- {
- return this == &other;
+ bool privateBrowsingEnabled = false;
+ if (context->isDocument()) {
+ if (auto* page = static_cast<Document*>(context)->page())
+ privateBrowsingEnabled = page->sessionID().isEphemeral();
}
-private:
- IndexPopulator(PassRefPtr<IDBDatabaseBackend> backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- : EventListener(CPPEventListenerType)
- , m_databaseBackend(backend)
- , m_transactionId(transactionId)
- , m_objectStoreId(objectStoreId)
- , m_indexMetadata(indexMetadata)
- {
+ if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) {
+ // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing.
+ return Exception { IDBDatabaseException::DataCloneError, ASCIILiteral("Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.") };
}
- virtual void handleEvent(ScriptExecutionContext*, Event* event)
- {
- ASSERT(event->type() == eventNames().successEvent);
- EventTarget* target = event->target();
- IDBRequest* request = static_cast<IDBRequest*>(target);
-
- RefPtr<IDBAny> cursorAny = request->result(ASSERT_NO_EXCEPTION);
- RefPtr<IDBCursorWithValue> cursor;
- if (cursorAny->type() == IDBAny::IDBCursorWithValueType)
- cursor = cursorAny->idbCursorWithValue();
-
- Vector<int64_t, 1> indexIds;
- indexIds.append(m_indexMetadata.id);
- if (cursor) {
- cursor->continueFunction(static_cast<IDBKey*>(0), ASSERT_NO_EXCEPTION);
-
- RefPtr<IDBKey> primaryKey = cursor->idbPrimaryKey();
- Deprecated::ScriptValue value = cursor->value();
-
- IDBObjectStore::IndexKeys indexKeys;
- generateIndexKeysForValue(request->requestState(), m_indexMetadata, value, &indexKeys);
-
- Vector<IDBObjectStore::IndexKeys, 1> indexKeysList;
- indexKeysList.append(indexKeys);
-
- m_databaseBackend->setIndexKeys(m_transactionId, m_objectStoreId, primaryKey, indexIds, indexKeysList);
- } else {
- // Now that we are done indexing, tell the backend to go
- // back to processing tasks of type NormalTask.
- m_databaseBackend->setIndexesReady(m_transactionId, m_objectStoreId, indexIds);
- m_databaseBackend.clear();
+ if (key && !key->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: The parameter is not a valid key.") };
+
+ bool usesInlineKeys = !!m_info.keyPath();
+ bool usesKeyGenerator = autoIncrement();
+ if (usesInlineKeys && inlineKeyCheck == InlineKeyCheck::Perform) {
+ if (key)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: The object store uses in-line keys and the key parameter was provided.") };
+
+ RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath().value());
+ if (keyPathKey && !keyPathKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path yielded a value that is not a valid key.") };
+
+ if (!keyPathKey) {
+ if (!usesKeyGenerator)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path did not yield a value.") };
+ if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath().value()))
+ return Exception { IDBDatabaseException::DataError };
}
- }
+ if (keyPathKey) {
+ ASSERT(!key);
+ key = keyPathKey;
+ }
+ } else if (!usesKeyGenerator && !key)
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to store record in an IDBObjectStore: The object store uses out-of-line keys and has no key generator and the key parameter was not provided.") };
- RefPtr<IDBDatabaseBackend> m_databaseBackend;
- const int64_t m_transactionId;
- const int64_t m_objectStoreId;
- const IDBIndexMetadata m_indexMetadata;
-};
+ return m_transaction.requestPutOrAdd(state, *this, key.get(), *serializedValue, overwriteMode);
}
-PassRefPtr<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, IDBKeyRange* keyRange)
{
- bool unique = false;
- options.get("unique", unique);
+ return doDelete(execState, keyRange);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doDelete(ExecState& execState, IDBKeyRange* keyRange)
+{
+ LOG(IndexedDB, "IDBObjectStore::deleteFunction");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
+ // the exception for an object store being deleted.
+ // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
+ // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
+ // Until this is sorted out, we'll agree with the test and the majority share browsers.
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The transaction is inactive or finished.") };
- bool multiEntry = false;
- options.get("multiEntry", multiEntry);
+ if (m_transaction.isReadOnly())
+ return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The transaction is read-only.") };
- return createIndex(context, name, keyPath, unique, multiEntry, ec);
+ IDBKeyRangeData keyRangeData(keyRange);
+ if (!keyRangeData.isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key range.") };
+
+ return m_transaction.requestDeleteRecord(execState, *this, keyRangeData);
}
-PassRefPtr<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, JSValue key)
{
- LOG(StorageAPI, "IDBObjectStore::createIndex");
- if (!m_transaction->isVersionChange() || m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- if (!keyPath.isValid()) {
- ec = IDBDatabaseException::SyntaxError;
- return 0;
- }
- if (name.isNull()) {
- ec = TypeError;
- return 0;
- }
- if (containsIndex(name)) {
- ec = IDBDatabaseException::ConstraintError;
- return 0;
- }
+ Ref<IDBKey> idbKey = scriptValueToIDBKey(execState, key);
+ if (!idbKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key.") };
+ return doDelete(execState, IDBKeyRange::create(WTFMove(idbKey)).ptr());
+}
- if (keyPath.type() == IDBKeyPath::ArrayType && multiEntry) {
- ec = IDBDatabaseException::InvalidAccessError;
- return 0;
- }
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::clear(ExecState& execState)
+{
+ LOG(IndexedDB, "IDBObjectStore::clear");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
+ // the exception for an object store being deleted.
+ // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
+ // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
+ // Until this is sorted out, we'll agree with the test and the majority share browsers.
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The transaction is inactive or finished.") };
- int64_t indexId = m_metadata.maxIndexId + 1;
- backendDB()->createIndex(m_transaction->id(), id(), indexId, name, keyPath, unique, multiEntry);
+ if (m_transaction.isReadOnly())
+ return Exception { IDBDatabaseException::ReadOnlyError, ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The transaction is read-only.") };
- ++m_metadata.maxIndexId;
+ return m_transaction.requestClearObjectStore(execState, *this);
+}
+
+ExceptionOr<Ref<IDBIndex>> IDBObjectStore::createIndex(ExecState&, const String& name, IDBKeyPath&& keyPath, const IndexParameters& parameters)
+{
+ LOG(IndexedDB, "IDBObjectStore::createIndex %s (keyPath: %s, unique: %i, multiEntry: %i)", name.utf8().data(), loggingString(keyPath).utf8().data(), parameters.unique, parameters.multiEntry);
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (!m_transaction.isVersionChange())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The database is not running a version change transaction.") };
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The transaction is inactive.")};
- IDBIndexMetadata metadata(name, indexId, keyPath, unique, multiEntry);
- RefPtr<IDBIndex> index = IDBIndex::create(metadata, this, m_transaction.get());
- m_indexMap.set(name, index);
- m_metadata.indexes.set(indexId, metadata);
+ if (m_info.hasIndex(name))
+ return Exception { IDBDatabaseException::ConstraintError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': An index with the specified name already exists.") };
- ASSERT(!ec);
- if (ec)
- return 0;
+ if (!isIDBKeyPathValid(keyPath))
+ return Exception { IDBDatabaseException::SyntaxError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument contains an invalid key path.") };
- RefPtr<IDBRequest> indexRequest = openCursor(context, static_cast<IDBKeyRange*>(0), IDBCursor::directionNext(), IDBDatabaseBackend::PreemptiveTask, ec);
- ASSERT(!ec);
- if (ec)
- return 0;
- indexRequest->preventPropagation();
+ if (name.isNull())
+ return Exception { TypeError };
- // This is kept alive by being the success handler of the request, which is in turn kept alive by the owning transaction.
- RefPtr<IndexPopulator> indexPopulator = IndexPopulator::create(backendDB(), m_transaction->id(), id(), metadata);
- indexRequest->setOnsuccess(indexPopulator);
+ if (parameters.multiEntry && WTF::holds_alternative<Vector<String>>(keyPath))
+ return Exception { IDBDatabaseException::InvalidAccessError, ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument was an array and the multiEntry option is true.") };
- return index.release();
+ // Install the new Index into the ObjectStore's info.
+ IDBIndexInfo info = m_info.createNewIndex(name, WTFMove(keyPath), parameters.unique, parameters.multiEntry);
+ m_transaction.database().didCreateIndexInfo(info);
+
+ // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
+ auto index = m_transaction.createIndex(*this, info);
+
+ Ref<IDBIndex> referencedIndex { *index };
+
+ Locker<Lock> locker(m_referencedIndexLock);
+ m_referencedIndexes.set(name, WTFMove(index));
+
+ return WTFMove(referencedIndex);
}
-PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<IDBIndex>> IDBObjectStore::index(const String& indexName)
{
- LOG(StorageAPI, "IDBObjectStore::index");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (m_transaction->isFinished()) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
+ LOG(IndexedDB, "IDBObjectStore::index");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
- IDBIndexMap::iterator it = m_indexMap.find(name);
- if (it != m_indexMap.end())
- return it->value;
+ if (!scriptExecutionContext())
+ return Exception { IDBDatabaseException::InvalidStateError }; // FIXME: Is this code tested? Is iteven reachable?
- int64_t indexId = findIndexId(name);
- if (indexId == IDBIndexMetadata::InvalidId) {
- ec = IDBDatabaseException::NotFoundError;
- return 0;
- }
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (m_transaction.isFinishedOrFinishing())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The transaction is finished.") };
+
+ Locker<Lock> locker(m_referencedIndexLock);
+ auto iterator = m_referencedIndexes.find(indexName);
+ if (iterator != m_referencedIndexes.end())
+ return Ref<IDBIndex> { *iterator->value };
+
+ auto* info = m_info.infoForExistingIndex(indexName);
+ if (!info)
+ return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The specified index was not found.") };
+
+ auto index = std::make_unique<IDBIndex>(*scriptExecutionContext(), *info, *this);
- const IDBIndexMetadata* indexMetadata(0);
- for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
- if (it->value.name == name) {
- indexMetadata = &it->value;
- break;
+ Ref<IDBIndex> referencedIndex { *index };
+
+ m_referencedIndexes.set(indexName, WTFMove(index));
+
+ return WTFMove(referencedIndex);
+}
+
+ExceptionOr<void> IDBObjectStore::deleteIndex(const String& name)
+{
+ LOG(IndexedDB, "IDBObjectStore::deleteIndex %s", name.utf8().data());
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isVersionChange())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The database is not running a version change transaction.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The transaction is inactive or finished.") };
+
+ if (!m_info.hasIndex(name))
+ return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The specified index was not found.") };
+
+ auto* info = m_info.infoForExistingIndex(name);
+ ASSERT(info);
+ m_transaction.database().didDeleteIndexInfo(*info);
+
+ m_info.deleteIndex(name);
+
+ {
+ Locker<Lock> locker(m_referencedIndexLock);
+ if (auto index = m_referencedIndexes.take(name)) {
+ index->markAsDeleted();
+ m_deletedIndexes.add(index->info().identifier(), WTFMove(index));
}
}
- ASSERT(indexMetadata);
- ASSERT(indexMetadata->id != IDBIndexMetadata::InvalidId);
- RefPtr<IDBIndex> index = IDBIndex::create(*indexMetadata, this, m_transaction.get());
- m_indexMap.set(name, index);
- return index.release();
+ m_transaction.deleteIndex(m_info.identifier(), name);
+
+ return { };
}
-void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, JSValue key)
{
- LOG(StorageAPI, "IDBObjectStore::deleteIndex");
- if (!m_transaction->isVersionChange() || m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return;
- }
- int64_t indexId = findIndexId(name);
- if (indexId == IDBIndexMetadata::InvalidId) {
- ec = IDBDatabaseException::NotFoundError;
- return;
- }
+ LOG(IndexedDB, "IDBObjectStore::count");
- backendDB()->deleteIndex(m_transaction->id(), id(), indexId);
+ Ref<IDBKey> idbKey = scriptValueToIDBKey(execState, key);
+ if (!idbKey->isValid())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The parameter is not a valid key.") };
- m_metadata.indexes.remove(indexId);
- IDBIndexMap::iterator it = m_indexMap.find(name);
- if (it != m_indexMap.end()) {
- it->value->markDeleted();
- m_indexMap.remove(name);
- }
+ return doCount(execState, IDBKeyRangeData(idbKey.ptr()));
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, IDBKeyRange* range)
{
- return openCursor(context, static_cast<IDBKeyRange*>(0), ec);
+ LOG(IndexedDB, "IDBObjectStore::count");
+
+ return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doCount(ExecState& execState, const IDBKeyRangeData& range)
{
- return openCursor(context, keyRange, IDBCursor::directionNext(), ec);
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
+ // the exception for an object store being deleted.
+ // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
+ // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
+ // Until this is sorted out, we'll agree with the test and the majority share browsers.
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The transaction is inactive or finished.") };
+
+ if (!range.isValid())
+ return Exception { IDBDatabaseException::DataError };
+
+ return m_transaction.requestCount(execState, *this, range);
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> count)
{
- return openCursor(context, key, IDBCursor::directionNext(), ec);
+ LOG(IndexedDB, "IDBObjectStore::getAll");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAll' on 'IDBObjectStore': The object store has been deleted.") };
+
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAll' on 'IDBObjectStore': The transaction is inactive or finished.") };
+
+ return m_transaction.requestGetAllObjectStoreRecords(execState, *this, range.get(), IndexedDB::GetAllType::Values, count);
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& direction, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, JSValue key, std::optional<uint32_t> count)
{
- return openCursor(context, range, direction, IDBDatabaseBackend::NormalTask, ec);
+ auto onlyResult = IDBKeyRange::only(execState, key);
+ if (onlyResult.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAll' on 'IDBObjectStore': The parameter is not a valid key.") };
+
+ return getAll(execState, onlyResult.releaseReturnValue(), count);
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& directionString, IDBDatabaseBackend::TaskType taskType, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> count)
{
- LOG(StorageAPI, "IDBObjectStore::openCursor");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
- }
- IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec);
- if (ec)
- return 0;
+ LOG(IndexedDB, "IDBObjectStore::getAllKeys");
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ if (m_deleted)
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBObjectStore': The object store has been deleted.") };
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- request->setCursorDetails(IndexedDB::CursorType::KeyAndValue, direction);
+ if (!m_transaction.isActive())
+ return Exception { IDBDatabaseException::TransactionInactiveError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBObjectStore': The transaction is inactive or finished.") };
- backendDB()->openCursor(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, direction, false, static_cast<IDBDatabaseBackend::TaskType>(taskType), request);
- return request.release();
+ return m_transaction.requestGetAllObjectStoreRecords(execState, *this, range.get(), IndexedDB::GetAllType::Keys, count);
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, JSValue key, std::optional<uint32_t> count)
{
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return openCursor(context, keyRange.release(), direction, ec);
+ auto onlyResult = IDBKeyRange::only(execState, key);
+ if (onlyResult.hasException())
+ return Exception { IDBDatabaseException::DataError, ASCIILiteral("Failed to execute 'getAllKeys' on 'IDBObjectStore': The parameter is not a valid key.") };
+
+ return getAllKeys(execState, onlyResult.releaseReturnValue(), count);
}
-PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, ExceptionCode& ec)
+void IDBObjectStore::markAsDeleted()
{
- LOG(StorageAPI, "IDBObjectStore::count");
- if (m_deleted) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+ m_deleted = true;
+}
+
+void IDBObjectStore::rollbackForVersionChangeAbort()
+{
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ String currentName = m_info.name();
+ m_info = m_originalInfo;
+
+ auto& databaseInfo = transaction().database().info();
+ auto* objectStoreInfo = databaseInfo.infoForExistingObjectStore(m_info.identifier());
+ if (!objectStoreInfo) {
+ m_info.rename(currentName);
+ m_deleted = true;
+ } else {
+ m_deleted = false;
+
+ HashSet<uint64_t> indexesToRemove;
+ for (auto indexIdentifier : objectStoreInfo->indexMap().keys()) {
+ if (!objectStoreInfo->hasIndex(indexIdentifier))
+ indexesToRemove.add(indexIdentifier);
+ }
+
+ for (auto indexIdentifier : indexesToRemove)
+ m_info.deleteIndex(indexIdentifier);
}
- if (!m_transaction->isActive()) {
- ec = IDBDatabaseException::TransactionInactiveError;
- return 0;
+
+ Locker<Lock> locker(m_referencedIndexLock);
+
+ Vector<uint64_t> identifiersToRemove;
+ for (auto& iterator : m_deletedIndexes) {
+ if (m_info.hasIndex(iterator.key)) {
+ auto name = iterator.value->info().name();
+ m_referencedIndexes.set(name, WTFMove(iterator.value));
+ identifiersToRemove.append(iterator.key);
+ }
}
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->count(m_transaction->id(), id(), IDBIndexMetadata::InvalidId, range, request);
- return request.release();
+
+ for (auto identifier : identifiersToRemove)
+ m_deletedIndexes.remove(identifier);
+
+ for (auto& index : m_referencedIndexes.values())
+ index->rollbackInfoForVersionChangeAbort();
}
-PassRefPtr<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+void IDBObjectStore::visitReferencedIndexes(SlotVisitor& visitor) const
{
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return count(context, keyRange.release(), ec);
+ Locker<Lock> locker(m_referencedIndexLock);
+ for (auto& index : m_referencedIndexes.values())
+ visitor.addOpaqueRoot(index.get());
+ for (auto& index : m_deletedIndexes.values())
+ visitor.addOpaqueRoot(index.get());
}
-void IDBObjectStore::transactionFinished()
+void IDBObjectStore::renameReferencedIndex(IDBIndex& index, const String& newName)
{
- ASSERT(m_transaction->isFinished());
+ LOG(IndexedDB, "IDBObjectStore::renameReferencedIndex");
+
+ auto* indexInfo = m_info.infoForExistingIndex(index.info().identifier());
+ ASSERT(indexInfo);
+ indexInfo->rename(newName);
+
+ ASSERT(m_referencedIndexes.contains(index.info().name()));
+ ASSERT(!m_referencedIndexes.contains(newName));
+ ASSERT(m_referencedIndexes.get(index.info().name()) == &index);
- // Break reference cycles.
- m_indexMap.clear();
+ m_referencedIndexes.set(newName, m_referencedIndexes.take(index.info().name()));
}
-int64_t IDBObjectStore::findIndexId(const String& name) const
+void IDBObjectStore::ref()
{
- for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) {
- if (it->value.name == name) {
- ASSERT(it->key != IDBIndexMetadata::InvalidId);
- return it->key;
- }
- }
- return IDBIndexMetadata::InvalidId;
+ m_transaction.ref();
}
-IDBDatabaseBackend* IDBObjectStore::backendDB() const
+void IDBObjectStore::deref()
{
- return m_transaction->backendDB();
+ m_transaction.deref();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStore.h b/Source/WebCore/Modules/indexeddb/IDBObjectStore.h
index da65a054f..3be56901b 100644
--- a/Source/WebCore/Modules/indexeddb/IDBObjectStore.h
+++ b/Source/WebCore/Modules/indexeddb/IDBObjectStore.h
@@ -1,131 +1,139 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBObjectStore_h
-#define IDBObjectStore_h
-
-#include "Dictionary.h"
-#include "IDBCursor.h"
-#include "IDBDatabaseMetadata.h"
-#include "IDBIndex.h"
-#include "IDBKey.h"
-#include "IDBKeyRange.h"
-#include "IDBRequest.h"
-#include "IDBTransaction.h"
-#include "ScriptWrappable.h"
-#include "SerializedScriptValue.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "ActiveDOMObject.h"
+#include "ExceptionOr.h"
+#include "IDBCursorDirection.h"
+#include "IDBKeyPath.h"
+#include "IDBObjectStoreInfo.h"
+#include <wtf/HashSet.h>
+
+namespace JSC {
+class ExecState;
+class JSValue;
+class SlotVisitor;
+}
+
namespace WebCore {
class DOMStringList;
-class IDBAny;
+class IDBIndex;
+class IDBKey;
+class IDBKeyRange;
+class IDBRequest;
+class IDBTransaction;
+
+struct IDBKeyRangeData;
-class IDBObjectStore : public ScriptWrappable, public RefCounted<IDBObjectStore> {
+namespace IndexedDB {
+enum class ObjectStoreOverwriteMode;
+}
+
+class IDBObjectStore final : public ActiveDOMObject {
public:
- static PassRefPtr<IDBObjectStore> create(const IDBObjectStoreMetadata& metadata, IDBTransaction* transaction)
- {
- return adoptRef(new IDBObjectStore(metadata, transaction));
- }
- ~IDBObjectStore() { }
-
- // Implement the IDBObjectStore IDL
- int64_t id() const { return m_metadata.id; }
- const String name() const { return m_metadata.name; }
- PassRefPtr<IDBAny> keyPathAny() const { return IDBAny::create(m_metadata.keyPath); }
- const IDBKeyPath keyPath() const { return m_metadata.keyPath; }
- PassRefPtr<DOMStringList> indexNames() const;
- PassRefPtr<IDBTransaction> transaction() const { return m_transaction; }
- bool autoIncrement() const { return m_metadata.autoIncrement; }
-
- PassRefPtr<IDBRequest> add(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&);
- PassRefPtr<IDBRequest> put(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, IDBDatabaseBackend::TaskType, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&);
-
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> add(JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> put(JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> clear(ScriptExecutionContext*, ExceptionCode&);
-
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext* context, const String& name, const String& keyPath, const Dictionary& options, ExceptionCode& ec) { return createIndex(context, name, IDBKeyPath(keyPath), options, ec); }
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext* context, const String& name, const Vector<String>& keyPath, const Dictionary& options, ExceptionCode& ec) { return createIndex(context, name, IDBKeyPath(keyPath), options, ec); }
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext*, const String& name, const IDBKeyPath&, const Dictionary&, ExceptionCode&);
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext*, const String& name, const IDBKeyPath&, bool unique, bool multiEntry, ExceptionCode&);
-
- PassRefPtr<IDBIndex> index(const String& name, ExceptionCode&);
- void deleteIndex(const String& name, ExceptionCode&);
-
- PassRefPtr<IDBRequest> count(ScriptExecutionContext* context, ExceptionCode& ec) { return count(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
-
- PassRefPtr<IDBRequest> put(IDBDatabaseBackend::PutMode, PassRefPtr<IDBAny> source, JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> put(IDBDatabaseBackend::PutMode, PassRefPtr<IDBAny> source, JSC::ExecState*, Deprecated::ScriptValue&, PassRefPtr<IDBKey>, ExceptionCode&);
- void markDeleted() { m_deleted = true; }
- void transactionFinished();
-
- IDBObjectStoreMetadata metadata() const { return m_metadata; }
- void setMetadata(const IDBObjectStoreMetadata& metadata) { m_metadata = metadata; }
-
- typedef Vector<RefPtr<IDBKey>> IndexKeys;
- typedef HashMap<String, IndexKeys> IndexKeyMap;
-
- IDBDatabaseBackend* backendDB() const;
+ IDBObjectStore(ScriptExecutionContext&, const IDBObjectStoreInfo&, IDBTransaction&);
+ ~IDBObjectStore();
+
+ const String& name() const;
+ ExceptionOr<void> setName(const String&);
+ const std::optional<IDBKeyPath>& keyPath() const;
+ RefPtr<DOMStringList> indexNames() const;
+ IDBTransaction& transaction();
+ bool autoIncrement() const;
+
+ struct IndexParameters {
+ bool unique;
+ bool multiEntry;
+ };
+
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, RefPtr<IDBKeyRange>, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, RefPtr<IDBKeyRange>, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> add(JSC::ExecState&, JSC::JSValue, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> put(JSC::ExecState&, JSC::JSValue, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> deleteFunction(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> deleteFunction(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> clear(JSC::ExecState&);
+ ExceptionOr<Ref<IDBIndex>> createIndex(JSC::ExecState&, const String& name, IDBKeyPath&&, const IndexParameters&);
+ ExceptionOr<Ref<IDBIndex>> index(const String& name);
+ ExceptionOr<void> deleteIndex(const String& name);
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> count);
+
+ ExceptionOr<Ref<IDBRequest>> putForCursorUpdate(JSC::ExecState&, JSC::JSValue, JSC::JSValue key);
+
+ void markAsDeleted();
+ bool isDeleted() const { return m_deleted; }
+
+ const IDBObjectStoreInfo& info() const { return m_info; }
+
+ void rollbackForVersionChangeAbort();
+
+ void ref();
+ void deref();
+
+ void visitReferencedIndexes(JSC::SlotVisitor&) const;
+ void renameReferencedIndex(IDBIndex&, const String& newName);
private:
- IDBObjectStore(const IDBObjectStoreMetadata&, IDBTransaction*);
+ enum class InlineKeyCheck { Perform, DoNotPerform };
+ ExceptionOr<Ref<IDBRequest>> putOrAdd(JSC::ExecState&, JSC::JSValue, RefPtr<IDBKey>, IndexedDB::ObjectStoreOverwriteMode, InlineKeyCheck);
+ ExceptionOr<Ref<IDBRequest>> doCount(JSC::ExecState&, const IDBKeyRangeData&);
+ ExceptionOr<Ref<IDBRequest>> doDelete(JSC::ExecState&, IDBKeyRange*);
+
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
+ bool hasPendingActivity() const final;
- int64_t findIndexId(const String& name) const;
- bool containsIndex(const String& name) const
- {
- return findIndexId(name) != IDBIndexMetadata::InvalidId;
- }
+ IDBObjectStoreInfo m_info;
+ IDBObjectStoreInfo m_originalInfo;
- IDBObjectStoreMetadata m_metadata;
- RefPtr<IDBTransaction> m_transaction;
- bool m_deleted;
+ // IDBObjectStore objects are always owned by their referencing IDBTransaction.
+ // ObjectStores will never outlive transactions so its okay to keep a raw C++ reference here.
+ IDBTransaction& m_transaction;
- typedef HashMap<String, RefPtr<IDBIndex>> IDBIndexMap;
- IDBIndexMap m_indexMap;
+ bool m_deleted { false };
+
+ mutable Lock m_referencedIndexLock;
+ HashMap<String, std::unique_ptr<IDBIndex>> m_referencedIndexes;
+ HashMap<uint64_t, std::unique_ptr<IDBIndex>> m_deletedIndexes;
};
} // namespace WebCore
-#endif
-
-#endif // IDBObjectStore_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl b/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl
index ae6350d57..96b00063d 100644
--- a/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBObjectStore.idl
@@ -23,30 +23,47 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+// NOTE: This type is specified as 'any' in the IndexedDB specification, but is always
+// constrained to this union.
+typedef (DOMString or sequence<DOMString>) IDBKeyPath;
+
[
Conditional=INDEXED_DATABASE,
- JSNoStaticTables,
- ImplementationLacksVTable,
+ GenerateIsReachable=Impl,
+ JSCustomMarkFunction,
+ SkipVTableValidation,
] interface IDBObjectStore {
- [TreatReturnedNullStringAs=Null] readonly attribute DOMString name;
- [ImplementedAs=keyPathAny] readonly attribute IDBAny keyPath;
+ [SetterMayThrowException] attribute DOMString name;
+ readonly attribute IDBKeyPath? keyPath;
readonly attribute DOMStringList indexNames;
readonly attribute IDBTransaction transaction;
readonly attribute boolean autoIncrement;
- [CallWith=ScriptState, RaisesException] IDBRequest put(any value, optional any key);
- [CallWith=ScriptState, RaisesException] IDBRequest add(any value, optional any key);
- [CallWith=ScriptExecutionContext, ImplementedAs=deleteFunction, RaisesException] IDBRequest delete(IDBKeyRange? keyRange);
- [CallWith=ScriptExecutionContext, ImplementedAs=deleteFunction, RaisesException] IDBRequest delete(any key);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(IDBKeyRange? key);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest get(any key);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest clear();
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(optional IDBKeyRange? range, optional DOMString direction);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest openCursor(any key, optional DOMString direction);
- [CallWith=ScriptExecutionContext, Custom, RaisesException] IDBIndex createIndex(DOMString name, sequence<DOMString> keyPath, optional Dictionary options);
- [CallWith=ScriptExecutionContext, Custom, RaisesException] IDBIndex createIndex(DOMString name, DOMString keyPath, optional Dictionary options);
- [RaisesException] IDBIndex index(DOMString name);
- [RaisesException] void deleteIndex(DOMString name);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(optional IDBKeyRange? range);
- [CallWith=ScriptExecutionContext, RaisesException] IDBRequest count(any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest put(any value, optional any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest add(any value, optional any key);
+ [CallWith=ScriptState, ImplementedAs=deleteFunction, MayThrowException] IDBRequest delete(IDBKeyRange? keyRange);
+ [CallWith=ScriptState, ImplementedAs=deleteFunction, MayThrowException] IDBRequest delete(any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest get(IDBKeyRange? key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest get(any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getKey(IDBKeyRange? key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getKey(any key);
+ [CallWith=ScriptState, MayThrowException] IDBRequest clear();
+ [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next");
+ [CallWith=ScriptState, MayThrowException] IDBRequest openCursor(any key, optional IDBCursorDirection direction = "next");
+ [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(optional IDBKeyRange? range = null, optional IDBCursorDirection direction = "next");
+ [CallWith=ScriptState, MayThrowException] IDBRequest openKeyCursor(any key, optional IDBCursorDirection direction = "next");
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAll(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAll(any key, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(optional IDBKeyRange? range = null, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBRequest getAllKeys(any key, [EnforceRange] optional unsigned long count);
+ [CallWith=ScriptState, MayThrowException] IDBIndex createIndex(DOMString name, (DOMString or sequence<DOMString>) keyPath, optional IDBIndexParameters options);
+ [MayThrowException] IDBIndex index(DOMString name);
+ [MayThrowException] void deleteIndex(DOMString name);
+ [CallWith=ScriptState, MayThrowException] IDBRequest count(optional IDBKeyRange? range = null);
+ [CallWith=ScriptState, MayThrowException] IDBRequest count(any key);
+};
+
+dictionary IDBIndexParameters {
+ boolean unique = false;
+ boolean multiEntry = false;
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h b/Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h
deleted file mode 100644
index a1500a4a6..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2012 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 GOOGLE 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 IDBObjectStoreMetadata_h
-#define IDBObjectStoreMetadata_h
-
-#include "IDBIndexMetadata.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-struct IDBObjectStoreMetadata {
- IDBObjectStoreMetadata()
- {
- }
-
- IDBObjectStoreMetadata(const String& name, int64_t id, const IDBKeyPath& keyPath, bool autoIncrement, int64_t maxIndexId)
- : name(name)
- , id(id)
- , keyPath(keyPath)
- , autoIncrement(autoIncrement)
- , maxIndexId(maxIndexId)
- {
- }
-
- String name;
- int64_t id;
- IDBKeyPath keyPath;
- bool autoIncrement;
- int64_t maxIndexId;
-
- static const int64_t InvalidId = -1;
-
- typedef HashMap<int64_t, IDBIndexMetadata> IndexMap;
- IndexMap indexes;
-
- IDBObjectStoreMetadata isolatedCopy() const;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBObjectStoreMetadata_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp
index e929c6c2b..6b79850be 100644
--- a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -28,128 +28,206 @@
#if ENABLE(INDEXED_DATABASE)
+#include "DOMError.h"
+#include "EventNames.h"
+#include "IDBConnectionProxy.h"
+#include "IDBConnectionToServer.h"
#include "IDBDatabase.h"
-#include "IDBDatabaseCallbacksImpl.h"
-#include "IDBPendingTransactionMonitor.h"
+#include "IDBError.h"
+#include "IDBRequestCompletionEvent.h"
+#include "IDBResultData.h"
+#include "IDBTransaction.h"
#include "IDBVersionChangeEvent.h"
#include "Logging.h"
#include "ScriptExecutionContext.h"
namespace WebCore {
-PassRefPtr<IDBOpenDBRequest> IDBOpenDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseCallbacks> callbacks, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness versionNullness)
+Ref<IDBOpenDBRequest> IDBOpenDBRequest::createDeleteRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier)
{
- RefPtr<IDBOpenDBRequest> request(adoptRef(new IDBOpenDBRequest(context, callbacks, transactionId, version, versionNullness)));
- request->suspendIfNeeded();
- return request.release();
+ return adoptRef(*new IDBOpenDBRequest(context, connectionProxy, databaseIdentifier, 0, IndexedDB::RequestType::Delete));
}
-IDBOpenDBRequest::IDBOpenDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseCallbacks> callbacks, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness versionNullness)
- : IDBRequest(context, IDBAny::createNull(), IDBDatabaseBackend::NormalTask, 0)
- , m_databaseCallbacks(callbacks)
- , m_transactionId(transactionId)
+Ref<IDBOpenDBRequest> IDBOpenDBRequest::createOpenRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version)
+{
+ return adoptRef(*new IDBOpenDBRequest(context, connectionProxy, databaseIdentifier, version, IndexedDB::RequestType::Open));
+}
+
+IDBOpenDBRequest::IDBOpenDBRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version, IndexedDB::RequestType requestType)
+ : IDBRequest(context, connectionProxy)
+ , m_databaseIdentifier(databaseIdentifier)
, m_version(version)
- , m_versionNullness(versionNullness)
{
- ASSERT(!m_result);
+ m_requestType = requestType;
}
IDBOpenDBRequest::~IDBOpenDBRequest()
{
+ ASSERT(currentThread() == originThreadID());
}
-EventTargetInterface IDBOpenDBRequest::eventTargetInterface() const
+void IDBOpenDBRequest::onError(const IDBResultData& data)
{
- return IDBOpenDBRequestEventTargetInterfaceType;
+ ASSERT(currentThread() == originThreadID());
+
+ m_domError = DOMError::create(data.error().name(), data.error().message());
+ enqueueEvent(IDBRequestCompletionEvent::create(eventNames().errorEvent, true, true, *this));
}
-void IDBOpenDBRequest::onBlocked(uint64_t oldVersion)
+void IDBOpenDBRequest::versionChangeTransactionDidFinish()
{
- LOG(StorageAPI, "IDBOpenDBRequest::onBlocked()");
- if (!shouldEnqueueEvent())
- return;
-
- enqueueEvent(IDBVersionChangeEvent::create(oldVersion, m_version, m_versionNullness, eventNames().blockedEvent));
+ ASSERT(currentThread() == originThreadID());
+
+ // 3.3.7 "versionchange" transaction steps
+ // When the transaction is finished, after firing complete/abort on the transaction, immediately set request's transaction property to null.
+ m_shouldExposeTransactionToDOM = false;
}
-void IDBOpenDBRequest::onUpgradeNeeded(uint64_t oldVersion, PassRefPtr<IDBDatabaseBackend> prpDatabaseBackend, const IDBDatabaseMetadata& metadata)
+void IDBOpenDBRequest::fireSuccessAfterVersionChangeCommit()
{
- LOG(StorageAPI, "IDBOpenDBRequest::onUpgradeNeeded()");
- if (m_contextStopped || !scriptExecutionContext()) {
- RefPtr<IDBDatabaseBackend> db = prpDatabaseBackend;
- db->abort(m_transactionId);
- db->close(m_databaseCallbacks);
- return;
- }
- if (!shouldEnqueueEvent())
- return;
+ LOG(IndexedDB, "IDBOpenDBRequest::fireSuccessAfterVersionChangeCommit() - %s", resourceIdentifier().loggingString().utf8().data());
+
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(hasPendingActivity());
+ m_transaction->addRequest(*this);
- ASSERT(m_databaseCallbacks);
+ auto event = IDBRequestCompletionEvent::create(eventNames().successEvent, false, false, *this);
+ m_openDatabaseSuccessEvent = &event.get();
- RefPtr<IDBDatabaseBackend> databaseBackend = prpDatabaseBackend;
+ enqueueEvent(WTFMove(event));
+}
- RefPtr<IDBDatabase> idbDatabase = IDBDatabase::create(scriptExecutionContext(), databaseBackend, m_databaseCallbacks);
- idbDatabase->setMetadata(metadata);
- m_databaseCallbacks->connect(idbDatabase.get());
- m_databaseCallbacks = 0;
+void IDBOpenDBRequest::fireErrorAfterVersionChangeCompletion()
+{
+ LOG(IndexedDB, "IDBOpenDBRequest::fireErrorAfterVersionChangeCompletion() - %s", resourceIdentifier().loggingString().utf8().data());
- IDBDatabaseMetadata oldMetadata(metadata);
- oldMetadata.version = oldVersion;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(hasPendingActivity());
- m_transaction = IDBTransaction::create(scriptExecutionContext(), m_transactionId, idbDatabase.get(), this, oldMetadata);
- m_result = IDBAny::create(idbDatabase.release());
+ IDBError idbError(IDBDatabaseException::AbortError);
+ m_domError = DOMError::create(idbError.name(), idbError.message());
+ setResultToUndefined();
- if (m_versionNullness == IndexedDB::VersionNullness::Null)
- m_version = 1;
- enqueueEvent(IDBVersionChangeEvent::create(oldVersion, m_version, m_versionNullness, eventNames().upgradeneededEvent));
+ m_transaction->addRequest(*this);
+ enqueueEvent(IDBRequestCompletionEvent::create(eventNames().errorEvent, true, true, *this));
}
-void IDBOpenDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackend> prpBackend, const IDBDatabaseMetadata& metadata)
+void IDBOpenDBRequest::cancelForStop()
{
- LOG(StorageAPI, "IDBOpenDBRequest::onSuccess()");
- if (!shouldEnqueueEvent())
- return;
+ connectionProxy().openDBRequestCancelled({ connectionProxy(), *this });
+}
- RefPtr<IDBDatabaseBackend> backend = prpBackend;
- RefPtr<IDBDatabase> idbDatabase;
- if (m_result) {
- idbDatabase = m_result->idbDatabase();
- ASSERT(idbDatabase);
- ASSERT(!m_databaseCallbacks);
- } else {
- ASSERT(m_databaseCallbacks);
- idbDatabase = IDBDatabase::create(scriptExecutionContext(), backend.release(), m_databaseCallbacks);
- m_databaseCallbacks->connect(idbDatabase.get());
- m_databaseCallbacks = 0;
- m_result = IDBAny::create(idbDatabase.get());
- }
- idbDatabase->setMetadata(metadata);
- enqueueEvent(Event::create(eventNames().successEvent, false, false));
+bool IDBOpenDBRequest::dispatchEvent(Event& event)
+{
+ ASSERT(currentThread() == originThreadID());
+
+ bool result = IDBRequest::dispatchEvent(event);
+
+ if (m_transaction && m_transaction->isVersionChange() && (event.type() == eventNames().errorEvent || event.type() == eventNames().successEvent))
+ m_transaction->database().connectionProxy().didFinishHandlingVersionChangeTransaction(m_transaction->database().databaseConnectionIdentifier(), *m_transaction);
+
+ return result;
}
-bool IDBOpenDBRequest::shouldEnqueueEvent() const
+void IDBOpenDBRequest::onSuccess(const IDBResultData& resultData)
{
- if (m_contextStopped || !scriptExecutionContext())
- return false;
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
- if (m_requestAborted)
- return false;
- return true;
+ LOG(IndexedDB, "IDBOpenDBRequest::onSuccess()");
+
+ ASSERT(currentThread() == originThreadID());
+
+ setResult(IDBDatabase::create(*scriptExecutionContext(), connectionProxy(), resultData));
+ m_readyState = ReadyState::Done;
+
+ enqueueEvent(IDBRequestCompletionEvent::create(eventNames().successEvent, false, false, *this));
}
-bool IDBOpenDBRequest::dispatchEvent(PassRefPtr<Event> event)
+void IDBOpenDBRequest::onUpgradeNeeded(const IDBResultData& resultData)
{
- // If the connection closed between onUpgradeNeeded and the delivery of the "success" event,
- // an "error" event should be fired instead.
- if (event->type() == eventNames().successEvent && m_result->type() == IDBAny::IDBDatabaseType && m_result->idbDatabase()->isClosePending()) {
- m_result.clear();
- onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed."));
- return false;
+ ASSERT(currentThread() == originThreadID());
+
+ Ref<IDBDatabase> database = IDBDatabase::create(*scriptExecutionContext(), connectionProxy(), resultData);
+ Ref<IDBTransaction> transaction = database->startVersionChangeTransaction(resultData.transactionInfo(), *this);
+
+ ASSERT(transaction->info().mode() == IDBTransactionMode::Versionchange);
+ ASSERT(transaction->originalDatabaseInfo());
+
+ uint64_t oldVersion = transaction->originalDatabaseInfo()->version();
+ uint64_t newVersion = transaction->info().newVersion();
+
+ LOG(IndexedDB, "IDBOpenDBRequest::onUpgradeNeeded() - current version is %" PRIu64 ", new is %" PRIu64, oldVersion, newVersion);
+
+ setResult(WTFMove(database));
+ m_readyState = ReadyState::Done;
+ m_transaction = WTFMove(transaction);
+ m_transaction->addRequest(*this);
+
+ enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, eventNames().upgradeneededEvent));
+}
+
+void IDBOpenDBRequest::onDeleteDatabaseSuccess(const IDBResultData& resultData)
+{
+ ASSERT(currentThread() == originThreadID());
+
+ uint64_t oldVersion = resultData.databaseInfo().version();
+
+ LOG(IndexedDB, "IDBOpenDBRequest::onDeleteDatabaseSuccess() - current version is %" PRIu64, oldVersion);
+
+ m_readyState = ReadyState::Done;
+ setResultToUndefined();
+
+ enqueueEvent(IDBVersionChangeEvent::create(oldVersion, 0, eventNames().successEvent));
+}
+
+void IDBOpenDBRequest::requestCompleted(const IDBResultData& data)
+{
+ LOG(IndexedDB, "IDBOpenDBRequest::requestCompleted");
+
+ ASSERT(currentThread() == originThreadID());
+
+ // If an Open request was completed after the page has navigated, leaving this request
+ // with a stopped script execution context, we need to message back to the server so it
+ // doesn't hang waiting on a database connection or transaction that will never exist.
+ if (m_contextStopped) {
+ switch (data.type()) {
+ case IDBResultType::OpenDatabaseSuccess:
+ connectionProxy().abortOpenAndUpgradeNeeded(data.databaseConnectionIdentifier(), IDBResourceIdentifier::emptyValue());
+ break;
+ case IDBResultType::OpenDatabaseUpgradeNeeded:
+ connectionProxy().abortOpenAndUpgradeNeeded(data.databaseConnectionIdentifier(), data.transactionInfo().identifier());
+ break;
+ default:
+ break;
+ }
+
+ return;
}
- return IDBRequest::dispatchEvent(event);
+ switch (data.type()) {
+ case IDBResultType::Error:
+ onError(data);
+ break;
+ case IDBResultType::OpenDatabaseSuccess:
+ onSuccess(data);
+ break;
+ case IDBResultType::OpenDatabaseUpgradeNeeded:
+ onUpgradeNeeded(data);
+ break;
+ case IDBResultType::DeleteDatabaseSuccess:
+ onDeleteDatabaseSuccess(data);
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+void IDBOpenDBRequest::requestBlocked(uint64_t oldVersion, uint64_t newVersion)
+{
+ ASSERT(currentThread() == originThreadID());
+
+ LOG(IndexedDB, "IDBOpenDBRequest::requestBlocked");
+ enqueueEvent(IDBVersionChangeEvent::create(oldVersion, newVersion, eventNames().blockedEvent));
}
} // namespace WebCore
-#endif
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h
index 86b1a0fbb..a15a1dc34 100644
--- a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h
+++ b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h
@@ -1,72 +1,74 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBOpenDBRequest_h
-#define IDBOpenDBRequest_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "IDBDatabaseIdentifier.h"
#include "IDBRequest.h"
-#include "IndexedDB.h"
namespace WebCore {
-class IDBDatabaseCallbacks;
+class IDBResultData;
-class IDBOpenDBRequest : public IDBRequest {
+class IDBOpenDBRequest final : public IDBRequest {
public:
- static PassRefPtr<IDBOpenDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness);
+ static Ref<IDBOpenDBRequest> createDeleteRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&);
+ static Ref<IDBOpenDBRequest> createOpenRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&, uint64_t version);
+
virtual ~IDBOpenDBRequest();
+
+ const IDBDatabaseIdentifier& databaseIdentifier() const { return m_databaseIdentifier; }
+ uint64_t version() const { return m_version; }
+
+ void requestCompleted(const IDBResultData&);
+ void requestBlocked(uint64_t oldVersion, uint64_t newVersion);
- using IDBRequest::onSuccess;
+ void versionChangeTransactionDidFinish();
+ void fireSuccessAfterVersionChangeCommit();
+ void fireErrorAfterVersionChangeCompletion();
- virtual void onBlocked(uint64_t existingVersion) override;
- virtual void onUpgradeNeeded(uint64_t oldVersion, PassRefPtr<IDBDatabaseBackend>, const IDBDatabaseMetadata&) override;
- virtual void onSuccess(PassRefPtr<IDBDatabaseBackend>, const IDBDatabaseMetadata&) override;
+private:
+ IDBOpenDBRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&, uint64_t version, IndexedDB::RequestType);
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const;
- virtual bool dispatchEvent(PassRefPtr<Event>) override;
+ bool dispatchEvent(Event&) final;
- DEFINE_ATTRIBUTE_EVENT_LISTENER(blocked);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(upgradeneeded);
+ void cancelForStop() final;
-protected:
- virtual bool shouldEnqueueEvent() const override;
+ void onError(const IDBResultData&);
+ void onSuccess(const IDBResultData&);
+ void onUpgradeNeeded(const IDBResultData&);
+ void onDeleteDatabaseSuccess(const IDBResultData&);
-private:
- IDBOpenDBRequest(ScriptExecutionContext*, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness);
+ bool isOpenDBRequest() const final { return true; }
- RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
- const int64_t m_transactionId;
- uint64_t m_version;
- IndexedDB::VersionNullness m_versionNullness;
+ IDBDatabaseIdentifier m_databaseIdentifier;
+ uint64_t m_version { 0 };
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBOpenDBRequest_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl
index e5479cce2..dd6e62a23 100644
--- a/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl
@@ -25,11 +25,10 @@
[
Conditional=INDEXED_DATABASE,
- EventTarget,
- JSNoStaticTables,
JSGenerateToJSObject,
- JSGenerateToNativeObject
+ JSGenerateToNativeObject,
+ SkipVTableValidation,
] interface IDBOpenDBRequest : IDBRequest {
- attribute EventListener onblocked;
- attribute EventListener onupgradeneeded;
+ attribute EventHandler onblocked;
+ attribute EventHandler onupgradeneeded;
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp b/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp
deleted file mode 100644
index e6741bdd3..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp
+++ /dev/null
@@ -1,70 +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.
- *
- * 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 "IDBPendingTransactionMonitor.h"
-#include "IDBTransaction.h"
-#include <mutex>
-#include <wtf/ThreadSpecific.h>
-
-using WTF::ThreadSpecific;
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-typedef Vector<RefPtr<IDBTransaction>> TransactionList;
-
-static ThreadSpecific<TransactionList>& transactions()
-{
- // FIXME: Move the Vector to ScriptExecutionContext to avoid dealing with
- // thread-local storage.
- static std::once_flag onceFlag;
- static ThreadSpecific<TransactionList>* transactions;
- std::call_once(onceFlag, []{
- transactions = new ThreadSpecific<TransactionList>;
- });
-
- return *transactions;
-}
-
-void IDBPendingTransactionMonitor::addNewTransaction(PassRefPtr<IDBTransaction> transaction)
-{
- transactions()->append(transaction);
-}
-
-void IDBPendingTransactionMonitor::deactivateNewTransactions()
-{
- ThreadSpecific<TransactionList>& list = transactions();
- for (size_t i = 0; i < list->size(); ++i) {
- RefPtr<IDBTransaction> transaction = list->at(i);
- transaction->setActive(false);
- }
- // FIXME: Exercise this call to clear() in a layout test.
- list->clear();
-}
-
-};
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h b/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h
deleted file mode 100644
index fef55fe3c..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.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.
- *
- * 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 IDBPendingTransactionMonitor_h
-#define IDBPendingTransactionMonitor_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include <wtf/Noncopyable.h>
-#include <wtf/Vector.h>
-
-namespace WebCore {
-
-class IDBTransaction;
-
-// This class keeps track of the transactions created during the current
-// Javascript execution context. Transactions have an internal |active| flag
-// which is set to true on creation, but must be set to false when control
-// returns to the event loop.
-
-class IDBPendingTransactionMonitor {
- WTF_MAKE_NONCOPYABLE(IDBPendingTransactionMonitor);
-public:
- static void addNewTransaction(PassRefPtr<IDBTransaction>);
- static void deactivateNewTransactions();
-
-private:
- IDBPendingTransactionMonitor();
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBPendingTransactionMonitor_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h b/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h
index 7ca7783e7..e80d593a1 100644
--- a/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h
+++ b/Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h
@@ -10,7 +10,7 @@
* 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
+ * 3. Neither the name of Apple 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.
*
@@ -25,8 +25,8 @@
* (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 IDBRecordIdentifier_h
-#define IDBRecordIdentifier_h
+
+#pragma once
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>
@@ -37,14 +37,14 @@ namespace WebCore {
class IDBRecordIdentifier : public RefCounted<IDBRecordIdentifier> {
public:
- static PassRefPtr<IDBRecordIdentifier> create(const Vector<char>& encodedPrimaryKey, int64_t version)
+ static Ref<IDBRecordIdentifier> create(const Vector<char>& encodedPrimaryKey, int64_t version)
{
- return adoptRef(new IDBRecordIdentifier(encodedPrimaryKey, version));
+ return adoptRef(*new IDBRecordIdentifier(encodedPrimaryKey, version));
}
- static PassRefPtr<IDBRecordIdentifier> create()
+ static Ref<IDBRecordIdentifier> create()
{
- return adoptRef(new IDBRecordIdentifier);
+ return adoptRef(*new IDBRecordIdentifier);
}
const Vector<char>& encodedPrimaryKey() const { return m_encodedPrimaryKey; }
@@ -74,6 +74,4 @@ private:
} // namespace WebCore
-#endif
-
-#endif // IDBRecordIdentifier_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBRequest.cpp b/Source/WebCore/Modules/indexeddb/IDBRequest.cpp
index 1bc170647..8b84cd19c 100644
--- a/Source/WebCore/Modules/indexeddb/IDBRequest.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBRequest.cpp
@@ -1,29 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015-2017 Apple 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.
*
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -31,519 +28,527 @@
#if ENABLE(INDEXED_DATABASE)
-#include "EventException.h"
-#include "EventListener.h"
+#include "DOMError.h"
+#include "Event.h"
#include "EventNames.h"
#include "EventQueue.h"
-#include "ExceptionCodePlaceholder.h"
#include "IDBBindingUtilities.h"
-#include "IDBCursorBackend.h"
-#include "IDBCursorWithValue.h"
+#include "IDBConnectionProxy.h"
+#include "IDBCursor.h"
#include "IDBDatabase.h"
+#include "IDBDatabaseException.h"
#include "IDBEventDispatcher.h"
-#include "IDBTransaction.h"
+#include "IDBIndex.h"
+#include "IDBKeyData.h"
+#include "IDBObjectStore.h"
+#include "IDBResultData.h"
+#include "JSDOMConvert.h"
#include "Logging.h"
+#include "ScopeGuard.h"
#include "ScriptExecutionContext.h"
+#include "ThreadSafeDataBuffer.h"
+#include <heap/StrongInlines.h>
+#include <wtf/Variant.h>
+
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
{
- RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, IDBDatabaseBackend::NormalTask, transaction)));
- request->suspendIfNeeded();
- // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
- if (transaction)
- transaction->registerRequest(request.get());
- return request.release();
+ return adoptRef(*new IDBRequest(context, objectStore, transaction));
}
-PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
{
- RefPtr<IDBRequest> request(adoptRef(new IDBRequest(context, source, taskType, transaction)));
- request->suspendIfNeeded();
- // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
- if (transaction)
- transaction->registerRequest(request.get());
- return request.release();
+ return adoptRef(*new IDBRequest(context, cursor, transaction));
}
-IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction)
- : ActiveDOMObject(context)
- , m_result(0)
- , m_errorCode(0)
- , m_contextStopped(false)
- , m_transaction(transaction)
- , m_readyState(PENDING)
- , m_requestAborted(false)
- , m_source(source)
- , m_taskType(taskType)
- , m_hasPendingActivity(true)
- , m_cursorType(IndexedDB::CursorType::KeyAndValue)
- , m_cursorDirection(IndexedDB::CursorDirection::Next)
- , m_cursorFinished(false)
- , m_pendingCursor(0)
- , m_didFireUpgradeNeededEvent(false)
- , m_preventPropagation(false)
- , m_requestState(context)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
{
+ return adoptRef(*new IDBRequest(context, index, transaction));
}
-IDBRequest::~IDBRequest()
+Ref<IDBRequest> IDBRequest::createObjectStoreGet(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction)
{
- ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !scriptExecutionContext());
+ return adoptRef(*new IDBRequest(context, objectStore, type, transaction));
}
-PassRefPtr<IDBAny> IDBRequest::result(ExceptionCode& ec) const
+Ref<IDBRequest> IDBRequest::createIndexGet(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
{
- if (m_readyState != DONE) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- return m_result;
+ return adoptRef(*new IDBRequest(context, index, requestedRecordType, transaction));
}
-PassRefPtr<DOMError> IDBRequest::error(ExceptionCode& ec) const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy)
+ : IDBActiveDOMObject(&context)
+ , m_resourceIdentifier(connectionProxy)
+ , m_connectionProxy(connectionProxy)
{
- if (m_readyState != DONE) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- return m_error;
+ suspendIfNeeded();
}
-unsigned short IDBRequest::errorCode(ExceptionCode& ec) const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_source(&objectStore)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- if (m_readyState != DONE) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
- }
- return m_errorCode;
+ suspendIfNeeded();
}
-PassRefPtr<IDBAny> IDBRequest::source() const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_pendingCursor(&cursor)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- return m_source;
+ suspendIfNeeded();
+
+ WTF::switchOn(cursor.source(),
+ [this] (const auto& value) { this->m_source = IDBRequest::Source { value }; }
+ );
+
+ cursor.setRequest(*this);
}
-PassRefPtr<IDBTransaction> IDBRequest::transaction() const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_source(&index)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- return m_transaction;
+ suspendIfNeeded();
}
-const String& IDBRequest::readyState() const
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction)
+ : IDBActiveDOMObject(&context)
+ , m_transaction(&transaction)
+ , m_resourceIdentifier(transaction.connectionProxy())
+ , m_source(&objectStore)
+ , m_requestedObjectStoreRecordType(type)
+ , m_connectionProxy(transaction.database().connectionProxy())
{
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
- DEFINE_STATIC_LOCAL(AtomicString, pending, ("pending", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(AtomicString, done, ("done", AtomicString::ConstructFromLiteral));
-
- if (m_readyState == PENDING)
- return pending;
+ suspendIfNeeded();
+}
- return done;
+IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
+ : IDBRequest(context, index, transaction)
+{
+ m_requestedIndexRecordType = requestedRecordType;
}
-void IDBRequest::markEarlyDeath()
+IDBRequest::~IDBRequest()
{
- ASSERT(m_readyState == PENDING);
- m_readyState = EarlyDeath;
- if (m_transaction)
- m_transaction->unregisterRequest(this);
+ ASSERT(currentThread() == originThreadID());
+
+ if (m_result) {
+ WTF::switchOn(m_result.value(),
+ [] (RefPtr<IDBCursor>& cursor) { cursor->clearRequest(); },
+ [] (const auto&) { }
+ );
+ }
}
-void IDBRequest::abort()
+ExceptionOr<std::optional<IDBRequest::Result>> IDBRequest::result() const
{
- ASSERT(!m_requestAborted);
- if (m_contextStopped || !scriptExecutionContext())
- return;
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
- if (m_readyState == DONE)
- return;
+ if (!isDone())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to read the 'result' property from 'IDBRequest': The request has not finished.") };
- // Enqueued events may be the only reference to this object.
- RefPtr<IDBRequest> self(this);
+ return std::optional<IDBRequest::Result> { m_result };
+}
- EventQueue& eventQueue = scriptExecutionContext()->eventQueue();
- for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
- bool removed = eventQueue.cancelEvent(*m_enqueuedEvents[i]);
- ASSERT_UNUSED(removed, removed);
- }
- m_enqueuedEvents.clear();
+ExceptionOr<DOMError*> IDBRequest::error() const
+{
+ ASSERT(currentThread() == originThreadID());
+
+ if (!isDone())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to read the 'error' property from 'IDBRequest': The request has not finished.") };
- m_errorCode = 0;
- m_error.clear();
- m_errorMessage = String();
- m_result.clear();
- onError(IDBDatabaseError::create(IDBDatabaseException::AbortError));
- m_requestAborted = true;
+ return m_domError.get();
}
-void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, IndexedDB::CursorDirection direction)
+void IDBRequest::setSource(IDBCursor& cursor)
{
- ASSERT(m_readyState == PENDING);
- ASSERT(!m_pendingCursor);
- m_cursorType = cursorType;
- m_cursorDirection = direction;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_cursorRequestNotifier);
+
+ m_source = Source { &cursor };
+ m_cursorRequestNotifier = std::make_unique<ScopeGuard>([this]() {
+ ASSERT(WTF::holds_alternative<RefPtr<IDBCursor>>(m_source.value()));
+ WTF::get<RefPtr<IDBCursor>>(m_source.value())->decrementOutstandingRequestCount();
+ });
}
-void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> cursor)
+void IDBRequest::setVersionChangeTransaction(IDBTransaction& transaction)
{
- ASSERT(m_readyState == DONE);
- ASSERT(scriptExecutionContext());
- ASSERT(m_transaction);
- ASSERT(!m_pendingCursor);
- ASSERT(cursor == getResultCursor());
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_transaction);
+ ASSERT(transaction.isVersionChange());
+ ASSERT(!transaction.isFinishedOrFinishing());
- m_pendingCursor = cursor;
- m_result.clear();
- m_readyState = PENDING;
- m_errorCode = 0;
- m_error.clear();
- m_errorMessage = String();
- m_transaction->registerRequest(this);
+ m_transaction = &transaction;
}
-PassRefPtr<IDBCursor> IDBRequest::getResultCursor()
+RefPtr<WebCore::IDBTransaction> IDBRequest::transaction() const
{
- if (!m_result)
+ ASSERT(currentThread() == originThreadID());
+ return m_shouldExposeTransactionToDOM ? m_transaction : nullptr;
+}
+
+uint64_t IDBRequest::sourceObjectStoreIdentifier() const
+{
+ ASSERT(currentThread() == originThreadID());
+
+ if (!m_source)
return 0;
- if (m_result->type() == IDBAny::IDBCursorType)
- return m_result->idbCursor();
- if (m_result->type() == IDBAny::IDBCursorWithValueType)
- return m_result->idbCursorWithValue();
- return 0;
+
+ return WTF::switchOn(m_source.value(),
+ [] (const RefPtr<IDBObjectStore>& objectStore) { return objectStore->info().identifier(); },
+ [] (const RefPtr<IDBIndex>& index) { return index->info().objectStoreIdentifier(); },
+ [] (const RefPtr<IDBCursor>&) { return 0; }
+ );
}
-void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, const Deprecated::ScriptValue& value)
+uint64_t IDBRequest::sourceIndexIdentifier() const
{
- ASSERT(m_readyState == PENDING);
- m_cursorKey = key;
- m_cursorPrimaryKey = primaryKey;
- m_cursorValue = value;
+ ASSERT(currentThread() == originThreadID());
- if (m_cursorType == IndexedDB::CursorType::KeyOnly) {
- m_result = IDBAny::create(cursor);
- return;
- }
+ if (!m_source)
+ return 0;
- m_result = IDBAny::create(IDBCursorWithValue::fromCursor(cursor));
+ return WTF::switchOn(m_source.value(),
+ [] (const RefPtr<IDBObjectStore>&) -> uint64_t { return 0; },
+ [] (const RefPtr<IDBIndex>& index) -> uint64_t { return index->info().identifier(); },
+ [] (const RefPtr<IDBCursor>&) -> uint64_t { return 0; }
+ );
}
-void IDBRequest::finishCursor()
+IndexedDB::ObjectStoreRecordType IDBRequest::requestedObjectStoreRecordType() const
{
- m_cursorFinished = true;
- if (m_readyState != PENDING)
- m_hasPendingActivity = false;
+ ASSERT(currentThread() == originThreadID());
+
+ return m_requestedObjectStoreRecordType;
}
-bool IDBRequest::shouldEnqueueEvent() const
+IndexedDB::IndexRecordType IDBRequest::requestedIndexRecordType() const
{
- if (m_contextStopped || !scriptExecutionContext())
- return false;
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
- if (m_requestAborted)
- return false;
- ASSERT(m_readyState == PENDING);
- ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_error && !m_result);
- return true;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_source);
+ ASSERT(WTF::holds_alternative<RefPtr<IDBIndex>>(m_source.value()));
+
+ return m_requestedIndexRecordType;
}
-void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
+EventTargetInterface IDBRequest::eventTargetInterface() const
{
- LOG(StorageAPI, "IDBRequest::onError()");
- if (!shouldEnqueueEvent())
- return;
+ ASSERT(currentThread() == originThreadID());
- m_errorCode = error->code();
- m_errorMessage = error->message();
- m_error = DOMError::create(IDBDatabaseException::getErrorName(error->idbCode()));
- m_pendingCursor.clear();
- enqueueEvent(Event::create(eventNames().errorEvent, true, true));
+ return IDBRequestEventTargetInterfaceType;
}
-static PassRefPtr<Event> createSuccessEvent()
+const char* IDBRequest::activeDOMObjectName() const
{
- return Event::create(eventNames().successEvent, false, false);
+ ASSERT(currentThread() == originThreadID());
+
+ return "IDBRequest";
}
-void IDBRequest::onSuccess(PassRefPtr<DOMStringList> domStringList)
+bool IDBRequest::canSuspendForDocumentSuspension() const
{
- LOG(StorageAPI, "IDBRequest::onSuccess(DOMStringList)");
- if (!shouldEnqueueEvent())
- return;
+ ASSERT(currentThread() == originThreadID());
+ return false;
+}
- m_result = IDBAny::create(domStringList);
- enqueueEvent(createSuccessEvent());
+bool IDBRequest::hasPendingActivity() const
+{
+ ASSERT(currentThread() == originThreadID() || mayBeGCThread());
+ return m_hasPendingActivity;
}
-void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackend> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
+void IDBRequest::stop()
{
- LOG(StorageAPI, "IDBRequest::onSuccess(IDBCursor)");
- if (!shouldEnqueueEvent())
- return;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_contextStopped);
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer);
- ASSERT(!m_pendingCursor);
- RefPtr<IDBCursor> cursor;
- switch (m_cursorType) {
- case IndexedDB::CursorType::KeyOnly:
- cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
- break;
- case IndexedDB::CursorType::KeyAndValue:
- cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
- break;
- default:
- ASSERT_NOT_REACHED();
- }
- setResultCursor(cursor, key, primaryKey, value);
+ cancelForStop();
+
+ removeAllEventListeners();
- enqueueEvent(createSuccessEvent());
+ m_contextStopped = true;
}
-void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
+void IDBRequest::cancelForStop()
{
- LOG(StorageAPI, "IDBRequest::onSuccess(IDBKey)");
- if (!shouldEnqueueEvent())
- return;
-
- if (idbKey && idbKey->isValid()) {
- DOMRequestState::Scope scope(m_requestState);
- m_result = IDBAny::create(idbKeyToScriptValue(requestState(), idbKey));
- } else
- m_result = IDBAny::createInvalid();
- enqueueEvent(createSuccessEvent());
+ // The base IDBRequest class has nothing additional to do here.
}
-void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer)
+void IDBRequest::enqueueEvent(Ref<Event>&& event)
{
- LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer)");
- if (!shouldEnqueueEvent())
+ ASSERT(currentThread() == originThreadID());
+ if (!scriptExecutionContext() || m_contextStopped)
return;
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
- onSuccessInternal(value);
+ event->setTarget(this);
+ scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
}
-#ifndef NDEBUG
-static PassRefPtr<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> source)
+bool IDBRequest::dispatchEvent(Event& event)
{
- if (source->type() == IDBAny::IDBObjectStoreType)
- return source->idbObjectStore();
- if (source->type() == IDBAny::IDBIndexType)
- return source->idbIndex()->objectStore();
+ LOG(IndexedDB, "IDBRequest::dispatchEvent - %s (%p)", event.type().string().utf8().data(), this);
+
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_hasPendingActivity);
+ ASSERT(!m_contextStopped);
+
+ if (event.type() != eventNames().blockedEvent)
+ m_readyState = ReadyState::Done;
+
+ Vector<RefPtr<EventTarget>> targets;
+ targets.append(this);
+
+ if (&event == m_openDatabaseSuccessEvent)
+ m_openDatabaseSuccessEvent = nullptr;
+ else if (m_transaction && !m_transaction->isFinished()) {
+ targets.append(m_transaction);
+ targets.append(m_transaction->db());
+ }
+
+ m_hasPendingActivity = false;
+
+ m_cursorRequestNotifier = nullptr;
+
+ bool dontPreventDefault;
+ {
+ TransactionActivator activator(m_transaction.get());
+ dontPreventDefault = IDBEventDispatcher::dispatch(event, targets);
+ }
+
+ // IDBEventDispatcher::dispatch() might have set the pending activity flag back to true, suggesting the request will be reused.
+ // We might also re-use the request if this event was the upgradeneeded event for an IDBOpenDBRequest.
+ if (!m_hasPendingActivity)
+ m_hasPendingActivity = isOpenDBRequest() && (event.type() == eventNames().upgradeneededEvent || event.type() == eventNames().blockedEvent);
+
+ // The request should only remain in the transaction's request list if it represents a pending cursor operation, or this is an open request that was blocked.
+ if (m_transaction && !m_pendingCursor && event.type() != eventNames().blockedEvent)
+ m_transaction->removeRequest(*this);
- ASSERT_NOT_REACHED();
- return 0;
+ if (dontPreventDefault && event.type() == eventNames().errorEvent && m_transaction && !m_transaction->isFinishedOrFinishing()) {
+ ASSERT(m_domError);
+ m_transaction->abortDueToFailedRequest(*m_domError);
+ }
+
+ if (m_transaction)
+ m_transaction->finishedDispatchEventForRequest(*this);
+
+ return dontPreventDefault;
}
-#endif
-void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath)
+void IDBRequest::uncaughtExceptionInEventHandler()
{
- LOG(StorageAPI, "IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
- if (!shouldEnqueueEvent())
- return;
+ LOG(IndexedDB, "IDBRequest::uncaughtExceptionInEventHandler");
-#ifndef NDEBUG
- ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath());
-#endif
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer);
+ ASSERT(currentThread() == originThreadID());
- RefPtr<IDBKey> primaryKey = prpPrimaryKey;
-#ifndef NDEBUG
- RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath);
- ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get()));
-#endif
- bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath);
- ASSERT_UNUSED(injected, injected);
- onSuccessInternal(value);
+ if (m_transaction && m_idbError.code() != IDBDatabaseException::AbortError)
+ m_transaction->abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError), ASCIILiteral("IDBTransaction will abort due to uncaught exception in an event handler")));
}
-void IDBRequest::onSuccess(int64_t value)
+void IDBRequest::setResult(const IDBKeyData& keyData)
{
- LOG(StorageAPI, "IDBRequest::onSuccess(int64_t)");
- if (!shouldEnqueueEvent())
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
return;
- return onSuccessInternal(SerializedScriptValue::numberValue(value));
-}
-void IDBRequest::onSuccess()
-{
- LOG(StorageAPI, "IDBRequest::onSuccess()");
- if (!shouldEnqueueEvent())
+ auto* state = context->execState();
+ if (!state)
return;
- return onSuccessInternal(SerializedScriptValue::undefinedValue());
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLIDBKeyData>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), keyData) } };
}
-void IDBRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value)
+void IDBRequest::setResult(const Vector<IDBKeyData>& keyDatas)
{
- ASSERT(!m_contextStopped);
- DOMRequestState::Scope scope(m_requestState);
- return onSuccessInternal(deserializeIDBValue(requestState(), value));
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ auto* state = context->execState();
+ if (!state)
+ return;
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLSequence<IDLIDBKeyData>>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), keyDatas) } };
}
-void IDBRequest::onSuccessInternal(const Deprecated::ScriptValue& value)
+void IDBRequest::setResult(const Vector<IDBValue>& values)
{
- m_result = IDBAny::create(value);
- if (m_pendingCursor) {
- m_pendingCursor->close();
- m_pendingCursor.clear();
- }
- enqueueEvent(createSuccessEvent());
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ auto* state = context->execState();
+ if (!state)
+ return;
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLSequence<IDLIDBValue>>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), values) } };
}
-void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> buffer)
+void IDBRequest::setResult(uint64_t number)
{
- LOG(StorageAPI, "IDBRequest::onSuccess(key, primaryKey, value)");
- if (!shouldEnqueueEvent())
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
return;
- DOMRequestState::Scope scope(m_requestState);
- Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), buffer);
- ASSERT(m_pendingCursor);
- setResultCursor(m_pendingCursor.release(), key, primaryKey, value);
- enqueueEvent(createSuccessEvent());
+ m_result = Result { JSC::Strong<JSC::Unknown> { context->vm(), toJS<IDLUnrestrictedDouble>(number) } };
}
-bool IDBRequest::hasPendingActivity() const
+void IDBRequest::setResultToStructuredClone(const IDBValue& value)
{
- // 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_hasPendingActivity && !m_contextStopped;
+ ASSERT(currentThread() == originThreadID());
+
+ LOG(IndexedDB, "IDBRequest::setResultToStructuredClone");
+
+ auto* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ auto* state = context->execState();
+ if (!state)
+ return;
+
+ // FIXME: This conversion should be done lazily, when script needs the JSValues, so that global object
+ // of the IDBRequest wrapper can be used, rather than the lexicalGlobalObject.
+ VM& vm = context->vm();
+ JSLockHolder lock(vm);
+ m_result = Result { JSC::Strong<JSC::Unknown> { vm, toJS<IDLIDBValue>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), value) } };
}
-void IDBRequest::stop()
+void IDBRequest::setResultToUndefined()
{
- if (m_contextStopped)
+ ASSERT(currentThread() == originThreadID());
+
+ auto* context = scriptExecutionContext();
+ if (!context)
return;
- m_contextStopped = true;
- m_requestState.clear();
- if (m_readyState == PENDING)
- markEarlyDeath();
+ m_result = Result { JSC::Strong<JSC::Unknown> { context->vm(), JSC::jsUndefined() } };
}
-EventTargetInterface IDBRequest::eventTargetInterface() const
+IDBCursor* IDBRequest::resultCursor()
{
- return IDBRequestEventTargetInterfaceType;
+ ASSERT(currentThread() == originThreadID());
+
+ if (!m_result)
+ return nullptr;
+
+ return WTF::switchOn(m_result.value(),
+ [] (const RefPtr<IDBCursor>& cursor) -> IDBCursor* { return cursor.get(); },
+ [] (const auto&) -> IDBCursor* { return nullptr; }
+ );
}
-bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
+void IDBRequest::willIterateCursor(IDBCursor& cursor)
{
- LOG(StorageAPI, "IDBRequest::dispatchEvent");
- ASSERT(m_readyState == PENDING);
- ASSERT(!m_contextStopped);
- ASSERT(m_hasPendingActivity);
- ASSERT(m_enqueuedEvents.size());
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(isDone());
ASSERT(scriptExecutionContext());
- ASSERT(event->target() == this);
- ASSERT_WITH_MESSAGE(m_readyState < DONE, "When dispatching event %s, m_readyState < DONE(%d), was %d", event->type().string().utf8().data(), DONE, m_readyState);
-
- DOMRequestState::Scope scope(m_requestState);
+ ASSERT(m_transaction);
+ ASSERT(!m_pendingCursor);
+ ASSERT(&cursor == resultCursor());
+ ASSERT(!m_cursorRequestNotifier);
- if (event->type() != eventNames().blockedEvent)
- m_readyState = DONE;
+ m_pendingCursor = &cursor;
+ m_hasPendingActivity = true;
+ m_result = std::nullopt;
+ m_readyState = ReadyState::Pending;
+ m_domError = nullptr;
+ m_idbError = { };
- for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
- if (m_enqueuedEvents[i].get() == event.get())
- m_enqueuedEvents.remove(i);
- }
+ m_cursorRequestNotifier = std::make_unique<ScopeGuard>([this]() {
+ m_pendingCursor->decrementOutstandingRequestCount();
+ });
+}
- Vector<RefPtr<EventTarget>> targets;
- targets.append(this);
- if (m_transaction && !m_preventPropagation) {
- 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());
- }
+void IDBRequest::didOpenOrIterateCursor(const IDBResultData& resultData)
+{
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(m_pendingCursor);
- // Cursor properties should not updated until the success event is being dispatched.
- RefPtr<IDBCursor> cursorToNotify;
- if (event->type() == eventNames().successEvent) {
- cursorToNotify = getResultCursor();
- if (cursorToNotify) {
- cursorToNotify->setValueReady(requestState(), m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue);
- m_cursorValue.clear();
- }
- }
+ m_result = std::nullopt;
- if (event->type() == eventNames().upgradeneededEvent) {
- ASSERT(!m_didFireUpgradeNeededEvent);
- m_didFireUpgradeNeededEvent = true;
+ if (resultData.type() == IDBResultType::IterateCursorSuccess || resultData.type() == IDBResultType::OpenCursorSuccess) {
+ m_pendingCursor->setGetResult(*this, resultData.getResult());
+ if (resultData.getResult().isDefined())
+ m_result = Result { m_pendingCursor };
}
- // FIXME: When we allow custom event dispatching, this will probably need to change.
- ASSERT_WITH_MESSAGE(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent || event->type() == eventNames().upgradeneededEvent, "event type was %s", event->type().string().utf8().data());
- const bool setTransactionActive = m_transaction && (event->type() == eventNames().successEvent || event->type() == eventNames().upgradeneededEvent || (event->type() == eventNames().errorEvent && m_errorCode != IDBDatabaseException::AbortError));
-
- if (setTransactionActive)
- m_transaction->setActive(true);
+ m_cursorRequestNotifier = nullptr;
+ m_pendingCursor = nullptr;
- bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
+ completeRequestAndDispatchEvent(resultData);
+}
- if (m_transaction) {
- if (m_readyState == DONE)
- m_transaction->unregisterRequest(this);
+void IDBRequest::completeRequestAndDispatchEvent(const IDBResultData& resultData)
+{
+ ASSERT(currentThread() == originThreadID());
- // Possibly abort the transaction. This must occur after unregistering (so this request
- // doesn't receive a second error) and before deactivating (which might trigger commit).
- if (event->type() == eventNames().errorEvent && dontPreventDefault && !m_requestAborted) {
- m_transaction->setError(m_error, m_errorMessage);
- m_transaction->abort(IGNORE_EXCEPTION);
- }
+ m_readyState = ReadyState::Done;
- // If this was the last request in the transaction's list, it may commit here.
- if (setTransactionActive)
- m_transaction->setActive(false);
- }
+ m_idbError = resultData.error();
+ if (!m_idbError.isNull())
+ onError();
+ else
+ onSuccess();
+}
- if (cursorToNotify)
- cursorToNotify->postSuccessHandlerCallback();
+void IDBRequest::onError()
+{
+ LOG(IndexedDB, "IDBRequest::onError");
- if (m_readyState == DONE && (!cursorToNotify || m_cursorFinished) && event->type() != eventNames().upgradeneededEvent)
- m_hasPendingActivity = false;
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!m_idbError.isNull());
- return dontPreventDefault;
+ m_domError = DOMError::create(m_idbError.name(), m_idbError.message());
+ enqueueEvent(Event::create(eventNames().errorEvent, true, true));
}
-void IDBRequest::uncaughtExceptionInEventHandler()
+void IDBRequest::onSuccess()
{
- if (m_transaction && !m_requestAborted) {
- m_transaction->setError(DOMError::create(IDBDatabaseException::getErrorName(IDBDatabaseException::AbortError)), "Uncaught exception in event handler.");
- m_transaction->abort(IGNORE_EXCEPTION);
- }
-}
+ LOG(IndexedDB, "IDBRequest::onSuccess");
+ ASSERT(currentThread() == originThreadID());
-void IDBRequest::transactionDidFinishAndDispatch()
-{
- ASSERT(m_transaction);
- ASSERT(m_transaction->isVersionChange());
- ASSERT(m_readyState == DONE);
- ASSERT(scriptExecutionContext());
- m_transaction.clear();
- m_readyState = PENDING;
+ enqueueEvent(Event::create(eventNames().successEvent, false, false));
}
-void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
+void IDBRequest::setResult(Ref<IDBDatabase>&& database)
{
- ASSERT(m_readyState == PENDING || m_readyState == DONE);
-
- if (m_contextStopped || !scriptExecutionContext())
- return;
-
- ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().string().utf8().data(), m_readyState);
-
- event->setTarget(this);
+ ASSERT(currentThread() == originThreadID());
- if (scriptExecutionContext()->eventQueue().enqueueEvent(event.get()))
- m_enqueuedEvents.append(event);
+ m_result = Result { RefPtr<IDBDatabase> { WTFMove(database) } };
}
} // namespace WebCore
-#endif
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBRequest.h b/Source/WebCore/Modules/indexeddb/IDBRequest.h
index 0b5a4be5d..3949889d7 100644
--- a/Source/WebCore/Modules/indexeddb/IDBRequest.h
+++ b/Source/WebCore/Modules/indexeddb/IDBRequest.h
@@ -1,169 +1,180 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBRequest_h
-#define IDBRequest_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
-#include "ActiveDOMObject.h"
-#include "DOMError.h"
-#include "DOMRequestState.h"
-#include "DOMStringList.h"
-#include "Event.h"
-#include "EventListener.h"
-#include "EventNames.h"
#include "EventTarget.h"
-#include "IDBAny.h"
-#include "IDBCallbacks.h"
-#include "IDBCursor.h"
-#include "IDBDatabaseBackend.h"
-#include "IDBDatabaseCallbacks.h"
-#include "ScriptWrappable.h"
+#include "ExceptionOr.h"
+#include "IDBActiveDOMObject.h"
+#include "IDBError.h"
+#include "IDBResourceIdentifier.h"
+#include "IndexedDB.h"
+#include <heap/Strong.h>
namespace WebCore {
+class DOMError;
+class Event;
+class IDBCursor;
+class IDBDatabase;
+class IDBIndex;
+class IDBKeyData;
+class IDBObjectStore;
+class IDBResultData;
class IDBTransaction;
+class IDBValue;
+class ScopeGuard;
+class ThreadSafeDataBuffer;
-typedef int ExceptionCode;
+namespace IDBClient {
+class IDBConnectionProxy;
+class IDBConnectionToServer;
+}
-class IDBRequest : public ScriptWrappable, public IDBCallbacks, public EventTargetWithInlineData, public ActiveDOMObject {
+class IDBRequest : public EventTargetWithInlineData, public IDBActiveDOMObject, public RefCounted<IDBRequest> {
public:
- static PassRefPtr<IDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*);
- static PassRefPtr<IDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType, IDBTransaction*);
+ static Ref<IDBRequest> create(ScriptExecutionContext&, IDBObjectStore&, IDBTransaction&);
+ static Ref<IDBRequest> create(ScriptExecutionContext&, IDBCursor&, IDBTransaction&);
+ static Ref<IDBRequest> create(ScriptExecutionContext&, IDBIndex&, IDBTransaction&);
+ static Ref<IDBRequest> createObjectStoreGet(ScriptExecutionContext&, IDBObjectStore&, IndexedDB::ObjectStoreRecordType, IDBTransaction&);
+ static Ref<IDBRequest> createIndexGet(ScriptExecutionContext&, IDBIndex&, IndexedDB::IndexRecordType, IDBTransaction&);
+
+ const IDBResourceIdentifier& resourceIdentifier() const { return m_resourceIdentifier; }
+
virtual ~IDBRequest();
- PassRefPtr<IDBAny> result(ExceptionCode&) const;
- unsigned short errorCode(ExceptionCode&) const;
- PassRefPtr<DOMError> error(ExceptionCode&) const;
- PassRefPtr<IDBAny> source() const;
- PassRefPtr<IDBTransaction> transaction() const;
- void preventPropagation() { m_preventPropagation = true; }
+ using Result = Variant<RefPtr<IDBCursor>, RefPtr<IDBDatabase>, JSC::Strong<JSC::Unknown>>;
+ ExceptionOr<std::optional<Result>> result() const;
+
+ using Source = Variant<RefPtr<IDBObjectStore>, RefPtr<IDBIndex>, RefPtr<IDBCursor>>;
+ const std::optional<Source>& source() const { return m_source; }
- // Defined in the IDL
- enum ReadyState {
- PENDING = 1,
- DONE = 2,
- EarlyDeath = 3
- };
+ ExceptionOr<DOMError*> error() const;
- const String& readyState() const;
+ RefPtr<IDBTransaction> transaction() const;
+
+ enum class ReadyState { Pending, Done };
+ ReadyState readyState() const { return m_readyState; }
- DEFINE_ATTRIBUTE_EVENT_LISTENER(success);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
+ bool isDone() const { return m_readyState == ReadyState::Done; }
- void markEarlyDeath();
- void setCursorDetails(IndexedDB::CursorType, IndexedDB::CursorDirection);
- void setPendingCursor(PassRefPtr<IDBCursor>);
- void finishCursor();
- void abort();
+ uint64_t sourceObjectStoreIdentifier() const;
+ uint64_t sourceIndexIdentifier() const;
+ IndexedDB::ObjectStoreRecordType requestedObjectStoreRecordType() const;
+ IndexedDB::IndexRecordType requestedIndexRecordType() const;
- // IDBCallbacks
- virtual void onError(PassRefPtr<IDBDatabaseError>);
- virtual void onSuccess(PassRefPtr<DOMStringList>);
- virtual void onSuccess(PassRefPtr<IDBCursorBackend>, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>);
- virtual void onSuccess(PassRefPtr<IDBKey>);
- virtual void onSuccess(PassRefPtr<SharedBuffer>);
- virtual void onSuccess(PassRefPtr<SharedBuffer>, PassRefPtr<IDBKey>, const IDBKeyPath&);
- virtual void onSuccess(int64_t);
- virtual void onSuccess();
- virtual void onSuccess(PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>);
- virtual void onSuccessWithPrefetch(const Vector<RefPtr<IDBKey>>&, const Vector<RefPtr<IDBKey>>&, const Vector<RefPtr<SharedBuffer>>&) { ASSERT_NOT_REACHED(); } // Not implemented. Callback should not reach the renderer side.
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
- // ActiveDOMObject
- virtual bool hasPendingActivity() const override;
+ using RefCounted::ref;
+ using RefCounted::deref;
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const override;
- virtual ScriptExecutionContext* scriptExecutionContext() const override final { return ActiveDOMObject::scriptExecutionContext(); }
- virtual void uncaughtExceptionInEventHandler() override final;
+ void completeRequestAndDispatchEvent(const IDBResultData&);
- using EventTarget::dispatchEvent;
- virtual bool dispatchEvent(PassRefPtr<Event>) override;
+ void setResult(const IDBKeyData&);
+ void setResult(const Vector<IDBKeyData>&);
+ void setResult(const Vector<IDBValue>&);
+ void setResult(uint64_t);
+ void setResultToStructuredClone(const IDBValue&);
+ void setResultToUndefined();
- void transactionDidFinishAndDispatch();
+ void willIterateCursor(IDBCursor&);
+ void didOpenOrIterateCursor(const IDBResultData&);
- using RefCounted<IDBCallbacks>::ref;
- using RefCounted<IDBCallbacks>::deref;
+ const IDBCursor* pendingCursor() const { return m_pendingCursor.get(); }
- IDBDatabaseBackend::TaskType taskType() { return m_taskType; }
+ void setSource(IDBCursor&);
+ void setVersionChangeTransaction(IDBTransaction&);
- DOMRequestState* requestState() { return &m_requestState; }
+ IndexedDB::RequestType requestType() const { return m_requestType; }
+
+ bool hasPendingActivity() const final;
protected:
- IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType, IDBTransaction*);
- void enqueueEvent(PassRefPtr<Event>);
- virtual bool shouldEnqueueEvent() const;
- void onSuccessInternal(PassRefPtr<SerializedScriptValue>);
- void onSuccessInternal(const Deprecated::ScriptValue&);
-
- RefPtr<IDBAny> m_result;
- unsigned short m_errorCode;
- String m_errorMessage;
- RefPtr<DOMError> m_error;
- bool m_contextStopped;
+ IDBRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&);
+
+ void enqueueEvent(Ref<Event>&&);
+ bool dispatchEvent(Event&) override;
+
+ void setResult(Ref<IDBDatabase>&&);
+
+ IDBClient::IDBConnectionProxy& connectionProxy() { return m_connectionProxy.get(); }
+
+ // FIXME: Protected data members aren't great for maintainability.
+ // Consider adding protected helper functions and making these private.
+ ReadyState m_readyState { ReadyState::Pending };
RefPtr<IDBTransaction> m_transaction;
- ReadyState m_readyState;
- bool m_requestAborted; // May be aborted by transaction then receive async onsuccess; ignore vs. assert.
+ bool m_shouldExposeTransactionToDOM { true };
+ RefPtr<DOMError> m_domError;
+ IndexedDB::RequestType m_requestType { IndexedDB::RequestType::Other };
+ bool m_contextStopped { false };
+ Event* m_openDatabaseSuccessEvent { nullptr };
private:
- // ActiveDOMObject
- virtual void stop() override;
+ IDBRequest(ScriptExecutionContext&, IDBObjectStore&, IDBTransaction&);
+ IDBRequest(ScriptExecutionContext&, IDBCursor&, IDBTransaction&);
+ IDBRequest(ScriptExecutionContext&, IDBIndex&, IDBTransaction&);
+ IDBRequest(ScriptExecutionContext&, IDBObjectStore&, IndexedDB::ObjectStoreRecordType, IDBTransaction&);
+ IDBRequest(ScriptExecutionContext&, IDBIndex&, IndexedDB::IndexRecordType, IDBTransaction&);
+
+ EventTargetInterface eventTargetInterface() const override;
+
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
+ void stop() final;
+ virtual void cancelForStop();
+
+ void refEventTarget() final { RefCounted::ref(); }
+ void derefEventTarget() final { RefCounted::deref(); }
+ void uncaughtExceptionInEventHandler() final;
- // EventTarget
- virtual void refEventTarget() override final { ref(); }
- virtual void derefEventTarget() override final { deref(); }
+ virtual bool isOpenDBRequest() const { return false; }
- PassRefPtr<IDBCursor> getResultCursor();
- void setResultCursor(PassRefPtr<IDBCursor>, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, const Deprecated::ScriptValue&);
+ void onError();
+ void onSuccess();
- RefPtr<IDBAny> m_source;
- const IDBDatabaseBackend::TaskType m_taskType;
+ IDBCursor* resultCursor();
- bool m_hasPendingActivity;
- Vector<RefPtr<Event>> m_enqueuedEvents;
+ IDBError m_idbError;
+ IDBResourceIdentifier m_resourceIdentifier;
+
+ std::optional<Result> m_result;
+ std::optional<Source> m_source;
+
+ bool m_hasPendingActivity { true };
+ IndexedDB::ObjectStoreRecordType m_requestedObjectStoreRecordType { IndexedDB::ObjectStoreRecordType::ValueOnly };
+ IndexedDB::IndexRecordType m_requestedIndexRecordType { IndexedDB::IndexRecordType::Key };
- // Only used if the result type will be a cursor.
- IndexedDB::CursorType m_cursorType;
- IndexedDB::CursorDirection m_cursorDirection;
- bool m_cursorFinished;
RefPtr<IDBCursor> m_pendingCursor;
- RefPtr<IDBKey> m_cursorKey;
- RefPtr<IDBKey> m_cursorPrimaryKey;
- Deprecated::ScriptValue m_cursorValue;
- bool m_didFireUpgradeNeededEvent;
- bool m_preventPropagation;
- DOMRequestState m_requestState;
+ std::unique_ptr<ScopeGuard> m_cursorRequestNotifier;
+
+ Ref<IDBClient::IDBConnectionProxy> m_connectionProxy;
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBRequest_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBRequest.idl b/Source/WebCore/Modules/indexeddb/IDBRequest.idl
index c2c7e52e4..c1ec27b3b 100644
--- a/Source/WebCore/Modules/indexeddb/IDBRequest.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBRequest.idl
@@ -11,7 +11,7 @@
* 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
+ * 3. Neither the name of Apple 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.
*
@@ -28,19 +28,22 @@
*/
[
- Conditional=INDEXED_DATABASE,
ActiveDOMObject,
- EventTarget,
- JSNoStaticTables,
- JSGenerateToJSObject,
- JSGenerateToNativeObject
+ Conditional=INDEXED_DATABASE,
+ GenerateIsReachable=Impl,
+ SkipVTableValidation,
] interface IDBRequest : EventTarget {
- [GetterRaisesException] readonly attribute IDBAny result;
- [GetterRaisesException] readonly attribute DOMError error;
- readonly attribute IDBAny source;
+ [GetterMayThrowException] readonly attribute (IDBCursor or IDBDatabase or any)? result;
+ [GetterMayThrowException] readonly attribute DOMError? error;
+ readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source;
readonly attribute IDBTransaction transaction;
- readonly attribute DOMString readyState;
+ readonly attribute IDBRequestReadyState readyState;
+
+ attribute EventHandler onsuccess;
+ attribute EventHandler onerror;
+};
- attribute EventListener onsuccess;
- attribute EventListener onerror;
+enum IDBRequestReadyState {
+ "pending",
+ "done"
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp
new file mode 100644
index 000000000..100a41e38
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBRequestCompletionEvent.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBRequestCompletionEvent::IDBRequestCompletionEvent(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest& request)
+ : Event(type, canBubble, cancelable)
+ , m_request(request)
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h
index 22bda5d49..64666435a 100644
--- a/Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h
+++ b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,34 +23,28 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBPendingDeleteCall_h
-#define IDBPendingDeleteCall_h
-
-#include "IDBCallbacks.h"
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefPtr.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "Event.h"
+#include "IDBRequest.h"
+
namespace WebCore {
-class IDBPendingDeleteCall {
+class IDBRequestCompletionEvent : public Event {
public:
- static PassOwnPtr<IDBPendingDeleteCall> create(PassRefPtr<IDBCallbacks> callbacks)
+ static Ref<Event> create(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest& request)
{
- return adoptPtr(new IDBPendingDeleteCall(callbacks));
+ return adoptRef(*new IDBRequestCompletionEvent(type, canBubble, cancelable, request));
}
- IDBCallbacks* callbacks() { return m_callbacks.get(); }
private:
- IDBPendingDeleteCall(PassRefPtr<IDBCallbacks> callbacks)
- : m_callbacks(callbacks)
- {
- }
- RefPtr<IDBCallbacks> m_callbacks;
+ IDBRequestCompletionEvent(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest&);
+
+ Ref<IDBRequest> m_request;
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBPendingDeleteCall_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBServerConnection.h b/Source/WebCore/Modules/indexeddb/IDBServerConnection.h
deleted file mode 100644
index 104a2069b..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBServerConnection.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBServerConnection_h
-#define IDBServerConnection_h
-
-#include "IDBCursorBackendOperations.h"
-#include "IDBDatabaseMetadata.h"
-#include "IDBGetResult.h"
-#include "IDBTransactionBackendOperations.h"
-#include "IndexedDB.h"
-#include <functional>
-#include <wtf/HashSet.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-class IDBDatabaseError;
-class IDBKey;
-class IDBTransactionBackend;
-
-struct IDBOpenCursorResult;
-struct IDBIndexMetadata;
-struct IDBObjectStoreMetadata;
-
-// This interface provides a single asynchronous layer between the web-facing frontend
-// and the I/O performing backend of IndexedDatabase.
-// If an operation's completion needs to be confirmed that must be done through use of a callback function.
-class IDBServerConnection : public RefCounted<IDBServerConnection> {
-public:
- virtual ~IDBServerConnection() { }
-
- virtual bool isClosed() = 0;
-
- typedef std::function<void (bool success)> BoolCallbackFunction;
-
- // Factory-level operations
- virtual void deleteDatabase(const String& name, BoolCallbackFunction successCallback) = 0;
-
- // Database-level operations
- typedef std::function<void (const IDBDatabaseMetadata&, bool success)> GetIDBDatabaseMetadataFunction;
- virtual void getOrEstablishIDBDatabaseMetadata(GetIDBDatabaseMetadataFunction) = 0;
- virtual void close() = 0;
-
- // Transaction-level operations
- virtual void openTransaction(int64_t transactionID, const HashSet<int64_t>& objectStoreIds, IndexedDB::TransactionMode, BoolCallbackFunction successCallback) = 0;
- virtual void beginTransaction(int64_t transactionID, std::function<void()> completionCallback) = 0;
- virtual void commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback) = 0;
- virtual void resetTransaction(int64_t transactionID, std::function<void()> completionCallback) = 0;
- virtual void rollbackTransaction(int64_t transactionID, std::function<void()> completionCallback) = 0;
-
- virtual void setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata&, IDBKey& primaryKey, const Vector<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
-
- virtual void createObjectStore(IDBTransactionBackend&, const CreateObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void createIndex(IDBTransactionBackend&, const CreateIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void deleteIndex(IDBTransactionBackend&, const DeleteIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void get(IDBTransactionBackend&, const GetOperation&, std::function<void(const IDBGetResult&, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void put(IDBTransactionBackend&, const PutOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void openCursor(IDBTransactionBackend&, const OpenCursorOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void count(IDBTransactionBackend&, const CountOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void deleteRange(IDBTransactionBackend&, const DeleteRangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void clearObjectStore(IDBTransactionBackend&, const ClearObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void deleteObjectStore(IDBTransactionBackend&, const DeleteObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void changeDatabaseVersion(IDBTransactionBackend&, const IDBDatabaseBackend::VersionChangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
-
- // Cursor-level operations
- virtual void cursorAdvance(IDBCursorBackend&, const CursorAdvanceOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void cursorIterate(IDBCursorBackend&, const CursorIterationOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBServerConnection_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp b/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp
index 5d2f3f875..1ee73d28d 100644
--- a/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -28,404 +28,1378 @@
#if ENABLE(INDEXED_DATABASE)
-#include "EventException.h"
+#include "DOMError.h"
+#include "DOMStringList.h"
+#include "DOMWindow.h"
+#include "Event.h"
+#include "EventNames.h"
#include "EventQueue.h"
-#include "ExceptionCodePlaceholder.h"
+#include "IDBCursorWithValue.h"
#include "IDBDatabase.h"
#include "IDBDatabaseException.h"
+#include "IDBError.h"
#include "IDBEventDispatcher.h"
+#include "IDBGetRecordData.h"
#include "IDBIndex.h"
+#include "IDBIterateCursorData.h"
+#include "IDBKeyData.h"
+#include "IDBKeyRangeData.h"
#include "IDBObjectStore.h"
#include "IDBOpenDBRequest.h"
-#include "IDBPendingTransactionMonitor.h"
+#include "IDBRequest.h"
+#include "IDBResultData.h"
+#include "IDBValue.h"
+#include "JSDOMWindowBase.h"
#include "Logging.h"
-#include "ScriptCallStack.h"
#include "ScriptExecutionContext.h"
+#include "ScriptState.h"
+#include "SerializedScriptValue.h"
+#include "TransactionOperation.h"
+#include <wtf/NeverDestroyed.h>
+
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBTransaction> IDBTransaction::create(ScriptExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db)
+Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info)
{
- IDBOpenDBRequest* openDBRequest = 0;
- RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, objectStoreNames, mode, db, openDBRequest, IDBDatabaseMetadata())));
- transaction->suspendIfNeeded();
- return transaction.release();
+ return adoptRef(*new IDBTransaction(database, info, nullptr));
}
-PassRefPtr<IDBTransaction> IDBTransaction::create(ScriptExecutionContext* context, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
+Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest& request)
{
- RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, Vector<String>(), IndexedDB::TransactionMode::VersionChange, db, openDBRequest, previousMetadata)));
- transaction->suspendIfNeeded();
- return transaction.release();
+ return adoptRef(*new IDBTransaction(database, info, &request));
}
-const AtomicString& IDBTransaction::modeReadOnly()
+IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest* request)
+ : IDBActiveDOMObject(database.scriptExecutionContext())
+ , m_database(database)
+ , m_info(info)
+ , m_pendingOperationTimer(*this, &IDBTransaction::pendingOperationTimerFired)
+ , m_completedOperationTimer(*this, &IDBTransaction::completedOperationTimerFired)
+ , m_openDBRequest(request)
+ , m_currentlyCompletingRequest(request)
+
{
- DEFINE_STATIC_LOCAL(AtomicString, readonly, ("readonly", AtomicString::ConstructFromLiteral));
- return readonly;
+ LOG(IndexedDB, "IDBTransaction::IDBTransaction - %s", m_info.loggingString().utf8().data());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (m_info.mode() == IDBTransactionMode::Versionchange) {
+ ASSERT(m_openDBRequest);
+ m_openDBRequest->setVersionChangeTransaction(*this);
+ m_startedOnServer = true;
+ } else {
+ activate();
+
+ auto* context = scriptExecutionContext();
+ ASSERT(context);
+
+ RefPtr<IDBTransaction> self;
+ JSC::VM& vm = context->vm();
+ vm.whenIdle([self, this]() {
+ deactivate();
+ });
+
+ establishOnServer();
+ }
+
+ suspendIfNeeded();
}
-const AtomicString& IDBTransaction::modeReadWrite()
+IDBTransaction::~IDBTransaction()
{
- DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("readwrite", AtomicString::ConstructFromLiteral));
- return readwrite;
+ ASSERT(currentThread() == m_database->originThreadID());
}
-const AtomicString& IDBTransaction::modeVersionChange()
+IDBClient::IDBConnectionProxy& IDBTransaction::connectionProxy()
{
- DEFINE_STATIC_LOCAL(AtomicString, versionchange, ("versionchange", AtomicString::ConstructFromLiteral));
- return versionchange;
+ return m_database->connectionProxy();
}
-const AtomicString& IDBTransaction::modeReadOnlyLegacy()
+Ref<DOMStringList> IDBTransaction::objectStoreNames() const
{
- DEFINE_STATIC_LOCAL(AtomicString, readonly, ("0", AtomicString::ConstructFromLiteral));
- return readonly;
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ const Vector<String> names = isVersionChange() ? m_database->info().objectStoreNames() : m_info.objectStores();
+
+ Ref<DOMStringList> objectStoreNames = DOMStringList::create();
+ for (auto& name : names)
+ objectStoreNames->append(name);
+
+ objectStoreNames->sort();
+ return objectStoreNames;
}
-const AtomicString& IDBTransaction::modeReadWriteLegacy()
+IDBDatabase* IDBTransaction::db()
{
- DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("1", AtomicString::ConstructFromLiteral));
- return readwrite;
+ ASSERT(currentThread() == m_database->originThreadID());
+ return m_database.ptr();
}
+DOMError* IDBTransaction::error() const
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+ return m_domError.get();
+}
-IDBTransaction::IDBTransaction(ScriptExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
- : ActiveDOMObject(context)
- , m_id(id)
- , m_database(db)
- , m_objectStoreNames(objectStoreNames)
- , m_openDBRequest(openDBRequest)
- , m_mode(mode)
- , m_state(Active)
- , m_hasPendingActivity(true)
- , m_contextStopped(false)
- , m_previousMetadata(previousMetadata)
+ExceptionOr<Ref<IDBObjectStore>> IDBTransaction::objectStore(const String& objectStoreName)
{
- if (mode == IndexedDB::TransactionMode::VersionChange) {
- // Not active until the callback.
- m_state = Inactive;
+ LOG(IndexedDB, "IDBTransaction::objectStore");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (!scriptExecutionContext())
+ return Exception { IDBDatabaseException::InvalidStateError };
+
+ if (isFinishedOrFinishing())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The transaction finished.") };
+
+ Locker<Lock> locker(m_referencedObjectStoreLock);
+
+ auto iterator = m_referencedObjectStores.find(objectStoreName);
+ if (iterator != m_referencedObjectStores.end())
+ return Ref<IDBObjectStore> { *iterator->value };
+
+ bool found = false;
+ for (auto& objectStore : m_info.objectStores()) {
+ if (objectStore == objectStoreName) {
+ found = true;
+ break;
+ }
}
- // We pass a reference of this object before it can be adopted.
- relaxAdoptionRequirement();
- if (m_state == Active)
- IDBPendingTransactionMonitor::addNewTransaction(this);
- m_database->transactionCreated(this);
+ auto* info = m_database->info().infoForExistingObjectStore(objectStoreName);
+ if (!info)
+ return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.") };
+
+ // Version change transactions are scoped to every object store in the database.
+ if (!info || (!found && !isVersionChange()))
+ return Exception { IDBDatabaseException::NotFoundError, ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.") };
+
+ auto objectStore = std::make_unique<IDBObjectStore>(*scriptExecutionContext(), *info, *this);
+ auto* rawObjectStore = objectStore.get();
+ m_referencedObjectStores.set(objectStoreName, WTFMove(objectStore));
+
+ return Ref<IDBObjectStore>(*rawObjectStore);
}
-IDBTransaction::~IDBTransaction()
+
+void IDBTransaction::abortDueToFailedRequest(DOMError& error)
{
- ASSERT(m_state == Finished || m_contextStopped);
- ASSERT(m_requestList.isEmpty() || m_contextStopped);
+ LOG(IndexedDB, "IDBTransaction::abortDueToFailedRequest");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (isFinishedOrFinishing())
+ return;
+
+ m_domError = &error;
+ internalAbort();
+}
+
+void IDBTransaction::transitionedToFinishing(IndexedDB::TransactionState state)
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT(!isFinishedOrFinishing());
+ m_state = state;
+ ASSERT(isFinishedOrFinishing());
}
-const String& IDBTransaction::mode() const
+ExceptionOr<void> IDBTransaction::abort()
{
- return modeToString(m_mode);
+ LOG(IndexedDB, "IDBTransaction::abort");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (isFinishedOrFinishing())
+ return Exception { IDBDatabaseException::InvalidStateError, ASCIILiteral("Failed to execute 'abort' on 'IDBTransaction': The transaction is inactive or finished.") };
+
+ internalAbort();
+
+ return { };
}
-void IDBTransaction::setError(PassRefPtr<DOMError> error, const String& errorMessage)
+void IDBTransaction::internalAbort()
{
- ASSERT(m_state != Finished);
- ASSERT(error);
+ LOG(IndexedDB, "IDBTransaction::internalAbort");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(!isFinishedOrFinishing());
+
+ m_database->willAbortTransaction(*this);
+
+ if (isVersionChange()) {
+ Locker<Lock> locker(m_referencedObjectStoreLock);
+
+ auto& info = m_database->info();
+ Vector<uint64_t> identifiersToRemove;
+ for (auto& iterator : m_deletedObjectStores) {
+ if (info.infoForExistingObjectStore(iterator.key)) {
+ auto name = iterator.value->info().name();
+ m_referencedObjectStores.set(name, WTFMove(iterator.value));
+ identifiersToRemove.append(iterator.key);
+ }
+ }
+
+ for (auto identifier : identifiersToRemove)
+ m_deletedObjectStores.remove(identifier);
- // The first error to be set is the true cause of the
- // transaction abort.
- if (!m_error) {
- m_error = error;
- m_errorMessage = errorMessage;
+ for (auto& objectStore : m_referencedObjectStores.values())
+ objectStore->rollbackForVersionChangeAbort();
}
+
+ transitionedToFinishing(IndexedDB::TransactionState::Aborting);
+
+ m_abortQueue.swap(m_pendingTransactionOperationQueue);
+
+ LOG(IndexedDBOperations, "IDB abort-on-server operation: Transaction %s", info().identifier().loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServerAndCancelRequests));
}
-PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionCode& ec)
+void IDBTransaction::abortInProgressOperations(const IDBError& error)
{
- if (m_state == Finished) {
- ec = IDBDatabaseException::InvalidStateError;
- return 0;
+ LOG(IndexedDB, "IDBTransaction::abortInProgressOperations");
+
+ Vector<RefPtr<IDBClient::TransactionOperation>> inProgressAbortVector;
+ inProgressAbortVector.reserveInitialCapacity(m_transactionOperationsInProgressQueue.size());
+ while (!m_transactionOperationsInProgressQueue.isEmpty())
+ inProgressAbortVector.uncheckedAppend(m_transactionOperationsInProgressQueue.takeFirst());
+
+ for (auto& operation : inProgressAbortVector) {
+ m_transactionOperationsInProgressQueue.append(operation.get());
+ m_currentlyCompletingRequest = nullptr;
+ operation->doComplete(IDBResultData::error(operation->identifier(), error));
}
- IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
- if (it != m_objectStoreMap.end())
- return it->value;
+ Vector<RefPtr<IDBClient::TransactionOperation>> completedOnServerAbortVector;
+ completedOnServerAbortVector.reserveInitialCapacity(m_completedOnServerQueue.size());
+ while (!m_completedOnServerQueue.isEmpty())
+ completedOnServerAbortVector.uncheckedAppend(m_completedOnServerQueue.takeFirst().first);
- if (!isVersionChange() && !m_objectStoreNames.contains(name)) {
- ec = IDBDatabaseException::NotFoundError;
- return 0;
+ for (auto& operation : completedOnServerAbortVector) {
+ m_currentlyCompletingRequest = nullptr;
+ operation->doComplete(IDBResultData::error(operation->identifier(), error));
}
- int64_t objectStoreId = m_database->findObjectStoreId(name);
- if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
- ASSERT(isVersionChange());
- ec = IDBDatabaseException::NotFoundError;
- return 0;
+ connectionProxy().forgetActiveOperations(inProgressAbortVector);
+}
+
+void IDBTransaction::abortOnServerAndCancelRequests(IDBClient::TransactionOperation& operation)
+{
+ LOG(IndexedDB, "IDBTransaction::abortOnServerAndCancelRequests");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(m_pendingTransactionOperationQueue.isEmpty());
+
+ m_database->connectionProxy().abortTransaction(*this);
+
+ ASSERT(m_transactionOperationMap.contains(operation.identifier()));
+ ASSERT(m_transactionOperationsInProgressQueue.last() == &operation);
+ m_transactionOperationMap.remove(operation.identifier());
+ m_transactionOperationsInProgressQueue.removeLast();
+
+ m_currentlyCompletingRequest = nullptr;
+
+ IDBError error(IDBDatabaseException::AbortError);
+
+ abortInProgressOperations(error);
+
+ for (auto& operation : m_abortQueue) {
+ m_currentlyCompletingRequest = nullptr;
+ m_transactionOperationsInProgressQueue.append(operation.get());
+ operation->doComplete(IDBResultData::error(operation->identifier(), error));
}
- const IDBDatabaseMetadata& metadata = m_database->metadata();
+ // Since we're aborting, it should be impossible to have queued any further operations.
+ ASSERT(m_pendingTransactionOperationQueue.isEmpty());
+}
- RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this);
- objectStoreCreated(name, objectStore);
- return objectStore.release();
+const char* IDBTransaction::activeDOMObjectName() const
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+ return "IDBTransaction";
}
-void IDBTransaction::objectStoreCreated(const String& name, PassRefPtr<IDBObjectStore> prpObjectStore)
+bool IDBTransaction::canSuspendForDocumentSuspension() const
{
- ASSERT(m_state != Finished);
- RefPtr<IDBObjectStore> objectStore = prpObjectStore;
- m_objectStoreMap.set(name, objectStore);
- if (isVersionChange())
- m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
+ ASSERT(currentThread() == m_database->originThreadID());
+ return false;
}
-void IDBTransaction::objectStoreDeleted(const String& name)
+bool IDBTransaction::hasPendingActivity() const
{
- ASSERT(m_state != Finished);
- ASSERT(isVersionChange());
- IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
- if (it != m_objectStoreMap.end()) {
- RefPtr<IDBObjectStore> objectStore = it->value;
- m_objectStoreMap.remove(name);
- objectStore->markDeleted();
- m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
- m_deletedObjectStores.add(objectStore);
- }
+ ASSERT(currentThread() == m_database->originThreadID() || mayBeGCThread());
+ return !m_contextStopped && m_state != IndexedDB::TransactionState::Finished;
}
-void IDBTransaction::setActive(bool active)
+void IDBTransaction::stop()
{
- ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false");
- if (m_state == Finishing)
+ LOG(IndexedDB, "IDBTransaction::stop - %s", m_info.loggingString().utf8().data());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ // IDBDatabase::stop() calls IDBTransaction::stop() for each of its active transactions.
+ // Since the order of calling ActiveDOMObject::stop() is random, we might already have been stopped.
+ if (m_contextStopped)
return;
- ASSERT(active != (m_state == Active));
- m_state = active ? Active : Inactive;
- if (!active && m_requestList.isEmpty())
- backendDB()->commit(m_id);
+ removeAllEventListeners();
+
+ m_contextStopped = true;
+
+ if (isFinishedOrFinishing())
+ return;
+
+ internalAbort();
+}
+
+bool IDBTransaction::isActive() const
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+ return m_state == IndexedDB::TransactionState::Active;
}
-void IDBTransaction::abort(ExceptionCode& ec)
+bool IDBTransaction::isFinishedOrFinishing() const
{
- if (m_state == Finishing || m_state == Finished) {
- ec = IDBDatabaseException::InvalidStateError;
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ return m_state == IndexedDB::TransactionState::Committing
+ || m_state == IndexedDB::TransactionState::Aborting
+ || m_state == IndexedDB::TransactionState::Finished;
+}
+
+void IDBTransaction::addRequest(IDBRequest& request)
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+ m_openRequests.add(&request);
+}
+
+void IDBTransaction::removeRequest(IDBRequest& request)
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(m_openRequests.contains(&request));
+ m_openRequests.remove(&request);
+}
+
+void IDBTransaction::scheduleOperation(RefPtr<IDBClient::TransactionOperation>&& operation)
+{
+ ASSERT(!m_transactionOperationMap.contains(operation->identifier()));
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_pendingTransactionOperationQueue.append(operation);
+ m_transactionOperationMap.set(operation->identifier(), WTFMove(operation));
+
+ schedulePendingOperationTimer();
+}
+
+void IDBTransaction::schedulePendingOperationTimer()
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (!m_pendingOperationTimer.isActive())
+ m_pendingOperationTimer.startOneShot(0);
+}
+
+void IDBTransaction::pendingOperationTimerFired()
+{
+ LOG(IndexedDB, "IDBTransaction::pendingOperationTimerFired (%p)", this);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (!m_startedOnServer)
+ return;
+
+ // If the last in-progress operation we've sent to the server is not an IDBRequest operation,
+ // then we have to wait until it completes before sending any more.
+ if (!m_transactionOperationsInProgressQueue.isEmpty() && !m_transactionOperationsInProgressQueue.last()->nextRequestCanGoToServer())
return;
- }
- m_state = Finishing;
+ // We want to batch operations together without spinning the runloop for performance,
+ // but don't want to affect responsiveness of the main thread.
+ // This number is a good compromise in ad-hoc testing.
+ static const size_t operationBatchLimit = 128;
+
+ for (size_t iterations = 0; !m_pendingTransactionOperationQueue.isEmpty() && iterations < operationBatchLimit; ++iterations) {
+ auto operation = m_pendingTransactionOperationQueue.takeFirst();
+ m_transactionOperationsInProgressQueue.append(operation.get());
+ operation->perform();
+
+ if (!operation->nextRequestCanGoToServer())
+ break;
- while (!m_requestList.isEmpty()) {
- RefPtr<IDBRequest> request = *m_requestList.begin();
- m_requestList.remove(request);
- request->abort();
}
- RefPtr<IDBTransaction> selfRef = this;
- backendDB()->abort(m_id);
+ if (!m_transactionOperationMap.isEmpty() || !m_openRequests.isEmpty())
+ return;
+
+ if (!isFinishedOrFinishing())
+ commit();
}
-IDBTransaction::OpenCursorNotifier::OpenCursorNotifier(PassRefPtr<IDBTransaction> transaction, IDBCursor* cursor)
- : m_transaction(transaction),
- m_cursor(cursor)
+void IDBTransaction::operationCompletedOnServer(const IDBResultData& data, IDBClient::TransactionOperation& operation)
{
- m_transaction->registerOpenCursor(m_cursor);
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(currentThread() == operation.originThreadID());
+
+ m_completedOnServerQueue.append({ &operation, data });
+ scheduleCompletedOperationTimer();
}
-IDBTransaction::OpenCursorNotifier::~OpenCursorNotifier()
+void IDBTransaction::scheduleCompletedOperationTimer()
{
- if (m_cursor)
- m_transaction->unregisterOpenCursor(m_cursor);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (!m_completedOperationTimer.isActive())
+ m_completedOperationTimer.startOneShot(0);
}
-void IDBTransaction::OpenCursorNotifier::cursorFinished()
+void IDBTransaction::completedOperationTimerFired()
{
- if (m_cursor) {
- m_transaction->unregisterOpenCursor(m_cursor);
- m_cursor = 0;
- m_transaction.clear();
- }
+ LOG(IndexedDB, "IDBTransaction::completedOperationTimerFired (%p)", this);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (m_completedOnServerQueue.isEmpty() || m_currentlyCompletingRequest)
+ return;
+
+ auto iterator = m_completedOnServerQueue.takeFirst();
+ iterator.first->doComplete(iterator.second);
+
+ if (!m_completedOnServerQueue.isEmpty() && !m_currentlyCompletingRequest)
+ scheduleCompletedOperationTimer();
+}
+
+void IDBTransaction::completeNoncursorRequest(IDBRequest& request, const IDBResultData& result)
+{
+ ASSERT(!m_currentlyCompletingRequest);
+
+ request.completeRequestAndDispatchEvent(result);
+
+ m_currentlyCompletingRequest = &request;
}
-void IDBTransaction::registerOpenCursor(IDBCursor* cursor)
+void IDBTransaction::completeCursorRequest(IDBRequest& request, const IDBResultData& result)
{
- m_openCursors.add(cursor);
+ ASSERT(!m_currentlyCompletingRequest);
+
+ request.didOpenOrIterateCursor(result);
+
+ m_currentlyCompletingRequest = &request;
}
-void IDBTransaction::unregisterOpenCursor(IDBCursor* cursor)
+void IDBTransaction::finishedDispatchEventForRequest(IDBRequest& request)
{
- m_openCursors.remove(cursor);
+ if (isFinishedOrFinishing())
+ return;
+
+ ASSERT_UNUSED(request, !m_currentlyCompletingRequest || m_currentlyCompletingRequest == &request);
+
+ m_currentlyCompletingRequest = nullptr;
+ scheduleCompletedOperationTimer();
}
-void IDBTransaction::closeOpenCursors()
+void IDBTransaction::commit()
{
- HashSet<IDBCursor*> cursors;
- cursors.swap(m_openCursors);
- for (HashSet<IDBCursor*>::iterator i = cursors.begin(); i != cursors.end(); ++i)
- (*i)->close();
+ LOG(IndexedDB, "IDBTransaction::commit");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(!isFinishedOrFinishing());
+
+ transitionedToFinishing(IndexedDB::TransactionState::Committing);
+ m_database->willCommitTransaction(*this);
+
+ LOG(IndexedDBOperations, "IDB commit operation: Transaction %s", info().identifier().loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::commitOnServer));
}
-void IDBTransaction::registerRequest(IDBRequest* request)
+void IDBTransaction::commitOnServer(IDBClient::TransactionOperation& operation)
{
- ASSERT(request);
- ASSERT(m_state == Active);
- m_requestList.add(request);
+ LOG(IndexedDB, "IDBTransaction::commitOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().commitTransaction(*this);
+
+ ASSERT(!m_transactionOperationsInProgressQueue.isEmpty());
+ ASSERT(m_transactionOperationsInProgressQueue.last() == &operation);
+ m_transactionOperationsInProgressQueue.removeLast();
+
+ ASSERT(m_transactionOperationMap.contains(operation.identifier()));
+ m_transactionOperationMap.remove(operation.identifier());
}
-void IDBTransaction::unregisterRequest(IDBRequest* request)
+void IDBTransaction::finishAbortOrCommit()
{
- ASSERT(request);
- // If we aborted the request, it will already have been removed.
- m_requestList.remove(request);
+ ASSERT(m_state != IndexedDB::TransactionState::Finished);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_state = IndexedDB::TransactionState::Finished;
}
-void IDBTransaction::onAbort(PassRefPtr<IDBDatabaseError> prpError)
+void IDBTransaction::didStart(const IDBError& error)
{
- LOG(StorageAPI, "IDBTransaction::onAbort");
- RefPtr<IDBDatabaseError> error = prpError;
- ASSERT(m_state != Finished);
+ LOG(IndexedDB, "IDBTransaction::didStart");
+ ASSERT(currentThread() == m_database->originThreadID());
- if (m_state != Finishing) {
- ASSERT(error.get());
- setError(DOMError::create(error->name()), error->message());
+ m_database->didStartTransaction(*this);
- // Abort was not triggered by front-end, so outstanding requests must
- // be aborted now.
- while (!m_requestList.isEmpty()) {
- RefPtr<IDBRequest> request = *m_requestList.begin();
- m_requestList.remove(request);
- request->abort();
- }
- m_state = Finishing;
+ m_startedOnServer = true;
+
+ // It's possible the transaction failed to start on the server.
+ // That equates to an abort.
+ if (!error.isNull()) {
+ didAbort(error);
+ return;
}
+ schedulePendingOperationTimer();
+}
+
+void IDBTransaction::notifyDidAbort(const IDBError& error)
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->didAbortTransaction(*this);
+ m_idbError = error;
+ fireOnAbort();
+
if (isVersionChange()) {
- for (IDBObjectStoreMetadataMap::iterator it = m_objectStoreCleanupMap.begin(); it != m_objectStoreCleanupMap.end(); ++it)
- it->key->setMetadata(it->value);
- m_database->setMetadata(m_previousMetadata);
- m_database->close();
+ ASSERT(m_openDBRequest);
+ m_openDBRequest->fireErrorAfterVersionChangeCompletion();
}
- m_objectStoreCleanupMap.clear();
- closeOpenCursors();
+}
- // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
- enqueueEvent(Event::create(eventNames().abortEvent, true, false));
- m_database->transactionFinished(this);
+void IDBTransaction::didAbort(const IDBError& error)
+{
+ LOG(IndexedDB, "IDBTransaction::didAbort");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (m_state == IndexedDB::TransactionState::Finished)
+ return;
+
+ notifyDidAbort(error);
+
+ finishAbortOrCommit();
}
-void IDBTransaction::onComplete()
+void IDBTransaction::didCommit(const IDBError& error)
{
- LOG(StorageAPI, "IDBTransaction::onComplete");
- ASSERT(m_state != Finished);
- m_state = Finishing;
- m_objectStoreCleanupMap.clear();
- closeOpenCursors();
+ LOG(IndexedDB, "IDBTransaction::didCommit");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(m_state == IndexedDB::TransactionState::Committing);
+
+ if (error.isNull()) {
+ m_database->didCommitTransaction(*this);
+ fireOnComplete();
+ } else {
+ m_database->willAbortTransaction(*this);
+ notifyDidAbort(error);
+ }
- // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
+ finishAbortOrCommit();
+}
+
+void IDBTransaction::fireOnComplete()
+{
+ LOG(IndexedDB, "IDBTransaction::fireOnComplete");
+ ASSERT(currentThread() == m_database->originThreadID());
enqueueEvent(Event::create(eventNames().completeEvent, false, false));
- m_database->transactionFinished(this);
}
-bool IDBTransaction::hasPendingActivity() const
+void IDBTransaction::fireOnAbort()
{
- // 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_hasPendingActivity && !m_contextStopped;
+ LOG(IndexedDB, "IDBTransaction::fireOnAbort");
+ ASSERT(currentThread() == m_database->originThreadID());
+ enqueueEvent(Event::create(eventNames().abortEvent, true, false));
}
-IndexedDB::TransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionCode& ec)
+void IDBTransaction::enqueueEvent(Ref<Event>&& event)
{
- if (modeString.isNull()
- || modeString == IDBTransaction::modeReadOnly())
- return IndexedDB::TransactionMode::ReadOnly;
- if (modeString == IDBTransaction::modeReadWrite())
- return IndexedDB::TransactionMode::ReadWrite;
+ ASSERT(m_state != IndexedDB::TransactionState::Finished);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (!scriptExecutionContext() || m_contextStopped)
+ return;
- ec = TypeError;
- return IndexedDB::TransactionMode::ReadOnly;
+ event->setTarget(this);
+ scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
}
-const AtomicString& IDBTransaction::modeToString(IndexedDB::TransactionMode mode)
+bool IDBTransaction::dispatchEvent(Event& event)
{
- switch (mode) {
- case IndexedDB::TransactionMode::ReadOnly:
- return IDBTransaction::modeReadOnly();
- break;
+ LOG(IndexedDB, "IDBTransaction::dispatchEvent");
- case IndexedDB::TransactionMode::ReadWrite:
- return IDBTransaction::modeReadWrite();
- break;
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(scriptExecutionContext());
+ ASSERT(!m_contextStopped);
+ ASSERT(event.target() == this);
+ ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent);
- case IndexedDB::TransactionMode::VersionChange:
- return IDBTransaction::modeVersionChange();
- break;
+ Vector<RefPtr<EventTarget>> targets;
+ targets.append(this);
+ targets.append(db());
+
+ bool result = IDBEventDispatcher::dispatch(event, targets);
+
+ if (isVersionChange()) {
+ ASSERT(m_openDBRequest);
+ m_openDBRequest->versionChangeTransactionDidFinish();
+
+ if (event.type() == eventNames().completeEvent) {
+ if (m_database->isClosingOrClosed())
+ m_openDBRequest->fireErrorAfterVersionChangeCompletion();
+ else
+ m_openDBRequest->fireSuccessAfterVersionChangeCommit();
+ }
+
+ m_openDBRequest = nullptr;
}
- ASSERT_NOT_REACHED();
- return IDBTransaction::modeReadOnly();
+ return result;
}
-bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
+Ref<IDBObjectStore> IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info)
{
- LOG(StorageAPI, "IDBTransaction::dispatchEvent");
- ASSERT(m_state != Finished);
- ASSERT(m_hasPendingActivity);
+ LOG(IndexedDB, "IDBTransaction::createObjectStore");
+ ASSERT(isVersionChange());
ASSERT(scriptExecutionContext());
- ASSERT(event->target() == this);
- m_state = Finished;
+ ASSERT(currentThread() == m_database->originThreadID());
- // Break reference cycles.
- for (IDBObjectStoreMap::iterator it = m_objectStoreMap.begin(); it != m_objectStoreMap.end(); ++it)
- it->value->transactionFinished();
- m_objectStoreMap.clear();
- for (IDBObjectStoreSet::iterator it = m_deletedObjectStores.begin(); it != m_deletedObjectStores.end(); ++it)
- (*it)->transactionFinished();
- m_deletedObjectStores.clear();
+ Locker<Lock> locker(m_referencedObjectStoreLock);
- Vector<RefPtr<EventTarget>> targets;
- targets.append(this);
- targets.append(db());
+ auto objectStore = std::make_unique<IDBObjectStore>(*scriptExecutionContext(), info, *this);
+ auto* rawObjectStore = objectStore.get();
+ m_referencedObjectStores.set(info.name(), WTFMove(objectStore));
+
+ LOG(IndexedDBOperations, "IDB create object store operation: %s", info.condensedLoggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateObjectStoreOnServer, &IDBTransaction::createObjectStoreOnServer, info));
+
+ return *rawObjectStore;
+}
+
+void IDBTransaction::createObjectStoreOnServer(IDBClient::TransactionOperation& operation, const IDBObjectStoreInfo& info)
+{
+ LOG(IndexedDB, "IDBTransaction::createObjectStoreOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(isVersionChange());
+
+ m_database->connectionProxy().createObjectStore(operation, info);
+}
+
+void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess || resultData.type() == IDBResultType::Error);
+}
+
+void IDBTransaction::renameObjectStore(IDBObjectStore& objectStore, const String& newName)
+{
+ LOG(IndexedDB, "IDBTransaction::renameObjectStore");
+
+ Locker<Lock> locker(m_referencedObjectStoreLock);
+
+ ASSERT(isVersionChange());
+ ASSERT(scriptExecutionContext());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT(m_referencedObjectStores.contains(objectStore.info().name()));
+ ASSERT(!m_referencedObjectStores.contains(newName));
+ ASSERT(m_referencedObjectStores.get(objectStore.info().name()) == &objectStore);
- // FIXME: When we allow custom event dispatching, this will probably need to change.
- ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent);
- bool returnValue = IDBEventDispatcher::dispatch(event.get(), targets);
- // FIXME: Try to construct a test where |this| outlives openDBRequest and we
- // get a crash.
- if (m_openDBRequest) {
- ASSERT(isVersionChange());
- m_openDBRequest->transactionDidFinishAndDispatch();
+ uint64_t objectStoreIdentifier = objectStore.info().identifier();
+
+ LOG(IndexedDBOperations, "IDB rename object store operation: %s to %s", objectStore.info().condensedLoggingString().utf8().data(), newName.utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didRenameObjectStoreOnServer, &IDBTransaction::renameObjectStoreOnServer, objectStoreIdentifier, newName));
+
+ m_referencedObjectStores.set(newName, m_referencedObjectStores.take(objectStore.info().name()));
+}
+
+void IDBTransaction::renameObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "IDBTransaction::renameObjectStoreOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(isVersionChange());
+
+ m_database->connectionProxy().renameObjectStore(operation, objectStoreIdentifier, newName);
+}
+
+void IDBTransaction::didRenameObjectStoreOnServer(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didRenameObjectStoreOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameObjectStoreSuccess || resultData.type() == IDBResultType::Error);
+}
+
+std::unique_ptr<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "IDBTransaction::createIndex");
+ ASSERT(isVersionChange());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (!scriptExecutionContext())
+ return nullptr;
+
+ LOG(IndexedDBOperations, "IDB create index operation: %s under object store %s", info.condensedLoggingString().utf8().data(), objectStore.info().condensedLoggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateIndexOnServer, &IDBTransaction::createIndexOnServer, info));
+
+ return std::make_unique<IDBIndex>(*scriptExecutionContext(), info, objectStore);
+}
+
+void IDBTransaction::createIndexOnServer(IDBClient::TransactionOperation& operation, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "IDBTransaction::createIndexOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(isVersionChange());
+
+ m_database->connectionProxy().createIndex(operation, info);
+}
+
+void IDBTransaction::didCreateIndexOnServer(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didCreateIndexOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (resultData.type() == IDBResultType::CreateIndexSuccess)
+ return;
+
+ ASSERT(resultData.type() == IDBResultType::Error);
+
+ // This operation might have failed because the transaction is already aborting.
+ if (m_state == IndexedDB::TransactionState::Aborting)
+ return;
+
+ // Otherwise, failure to create an index forced abortion of the transaction.
+ abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(resultData.error().code()), resultData.error().message()));
+}
+
+void IDBTransaction::renameIndex(IDBIndex& index, const String& newName)
+{
+ LOG(IndexedDB, "IDBTransaction::renameIndex");
+ Locker<Lock> locker(m_referencedObjectStoreLock);
+
+ ASSERT(isVersionChange());
+ ASSERT(scriptExecutionContext());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT(m_referencedObjectStores.contains(index.objectStore().info().name()));
+ ASSERT(m_referencedObjectStores.get(index.objectStore().info().name()) == &index.objectStore());
+
+ index.objectStore().renameReferencedIndex(index, newName);
+
+ uint64_t objectStoreIdentifier = index.objectStore().info().identifier();
+ uint64_t indexIdentifier = index.info().identifier();
+
+ LOG(IndexedDBOperations, "IDB rename index operation: %s to %s under object store %" PRIu64, index.info().condensedLoggingString().utf8().data(), newName.utf8().data(), index.info().objectStoreIdentifier());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didRenameIndexOnServer, &IDBTransaction::renameIndexOnServer, objectStoreIdentifier, indexIdentifier, newName));
+}
+
+void IDBTransaction::renameIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const uint64_t& indexIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "IDBTransaction::renameIndexOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(isVersionChange());
+
+ m_database->connectionProxy().renameIndex(operation, objectStoreIdentifier, indexIdentifier, newName);
+}
+
+void IDBTransaction::didRenameIndexOnServer(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didRenameIndexOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameIndexSuccess || resultData.type() == IDBResultType::Error);
+}
+
+Ref<IDBRequest> IDBTransaction::requestOpenCursor(ExecState& state, IDBObjectStore& objectStore, const IDBCursorInfo& info)
+{
+ LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (info.cursorType() == IndexedDB::CursorType::KeyOnly)
+ return doRequestOpenCursor(state, IDBCursor::create(*this, objectStore, info));
+
+ return doRequestOpenCursor(state, IDBCursorWithValue::create(*this, objectStore, info));
+}
+
+Ref<IDBRequest> IDBTransaction::requestOpenCursor(ExecState& state, IDBIndex& index, const IDBCursorInfo& info)
+{
+ LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (info.cursorType() == IndexedDB::CursorType::KeyOnly)
+ return doRequestOpenCursor(state, IDBCursor::create(*this, index, info));
+
+ return doRequestOpenCursor(state, IDBCursorWithValue::create(*this, index, info));
+}
+
+Ref<IDBRequest> IDBTransaction::doRequestOpenCursor(ExecState& state, Ref<IDBCursor>&& cursor)
+{
+ ASSERT(isActive());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), cursor.get(), *this);
+ addRequest(request.get());
+
+ LOG(IndexedDBOperations, "IDB open cursor operation: %s", cursor->info().loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didOpenCursorOnServer, &IDBTransaction::openCursorOnServer, cursor->info()));
+
+ return request;
+}
+
+void IDBTransaction::openCursorOnServer(IDBClient::TransactionOperation& operation, const IDBCursorInfo& info)
+{
+ LOG(IndexedDB, "IDBTransaction::openCursorOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().openCursor(operation, info);
+}
+
+void IDBTransaction::didOpenCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didOpenCursorOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ completeCursorRequest(request, resultData);
+}
+
+void IDBTransaction::iterateCursor(IDBCursor& cursor, const IDBIterateCursorData& data)
+{
+ LOG(IndexedDB, "IDBTransaction::iterateCursor");
+ ASSERT(isActive());
+ ASSERT(cursor.request());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ addRequest(*cursor.request());
+
+ LOG(IndexedDBOperations, "IDB iterate cursor operation: %s %s", cursor.info().loggingString().utf8().data(), data.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, *cursor.request(), &IDBTransaction::didIterateCursorOnServer, &IDBTransaction::iterateCursorOnServer, data));
+}
+
+// FIXME: changes here
+void IDBTransaction::iterateCursorOnServer(IDBClient::TransactionOperation& operation, const IDBIterateCursorData& data)
+{
+ LOG(IndexedDB, "IDBTransaction::iterateCursorOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().iterateCursor(operation, data);
+}
+
+void IDBTransaction::didIterateCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didIterateCursorOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ completeCursorRequest(request, resultData);
+}
+
+Ref<IDBRequest> IDBTransaction::requestGetAllObjectStoreRecords(JSC::ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, std::optional<uint32_t> count)
+{
+ LOG(IndexedDB, "IDBTransaction::requestGetAllObjectStoreRecords");
+ ASSERT(isActive());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
+ addRequest(request.get());
+
+ IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, objectStore.info().identifier(), 0 };
+
+ LOG(IndexedDBOperations, "IDB get all object store records operation: %s", getAllRecordsData.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetAllRecordsOnServer, &IDBTransaction::getAllRecordsOnServer, getAllRecordsData));
+
+ return request;
+}
+
+Ref<IDBRequest> IDBTransaction::requestGetAllIndexRecords(JSC::ExecState& state, IDBIndex& index, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, std::optional<uint32_t> count)
+{
+ LOG(IndexedDB, "IDBTransaction::requestGetAllIndexRecords");
+ ASSERT(isActive());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), index, *this);
+ addRequest(request.get());
+
+ IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, index.objectStore().info().identifier(), index.info().identifier() };
+
+ LOG(IndexedDBOperations, "IDB get all index records operation: %s", getAllRecordsData.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetAllRecordsOnServer, &IDBTransaction::getAllRecordsOnServer, getAllRecordsData));
+
+ return request;
+}
+
+void IDBTransaction::getAllRecordsOnServer(IDBClient::TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData)
+{
+ LOG(IndexedDB, "IDBTransaction::getAllRecordsOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().getAllRecords(operation, getAllRecordsData);
+}
+
+void IDBTransaction::didGetAllRecordsOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didGetAllRecordsOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (resultData.type() == IDBResultType::Error) {
+ completeNoncursorRequest(request, resultData);
+ return;
+ }
+
+ ASSERT(resultData.type() == IDBResultType::GetAllRecordsSuccess);
+
+ auto& getAllResult = resultData.getAllResult();
+ switch (getAllResult.type()) {
+ case IndexedDB::GetAllType::Keys:
+ request.setResult(getAllResult.keys());
+ break;
+ case IndexedDB::GetAllType::Values:
+ request.setResult(getAllResult.values());
+ break;
}
- m_hasPendingActivity = false;
- return returnValue;
+
+ completeNoncursorRequest(request, resultData);
}
-bool IDBTransaction::canSuspend() const
+Ref<IDBRequest> IDBTransaction::requestGetRecord(ExecState& state, IDBObjectStore& objectStore, const IDBGetRecordData& getRecordData)
{
- // FIXME: Technically we can suspend before the first request is schedule
- // and after the complete/abort event is enqueued.
- return m_state == Finished;
+ LOG(IndexedDB, "IDBTransaction::requestGetRecord");
+ ASSERT(isActive());
+ ASSERT(!getRecordData.keyRangeData.isNull);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ IndexedDB::ObjectStoreRecordType type = getRecordData.type == IDBGetRecordDataType::KeyAndValue ? IndexedDB::ObjectStoreRecordType::ValueOnly : IndexedDB::ObjectStoreRecordType::KeyOnly;
+
+ auto request = IDBRequest::createObjectStoreGet(*scriptExecutionContext(), objectStore, type, *this);
+ addRequest(request.get());
+
+ LOG(IndexedDBOperations, "IDB get record operation: %s %s", objectStore.info().condensedLoggingString().utf8().data(), getRecordData.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, getRecordData));
+
+ return request;
}
-void IDBTransaction::stop()
+Ref<IDBRequest> IDBTransaction::requestGetValue(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
{
- m_contextStopped = true;
+ LOG(IndexedDB, "IDBTransaction::requestGetValue");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Value, range);
+}
+
+Ref<IDBRequest> IDBTransaction::requestGetKey(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
+{
+ LOG(IndexedDB, "IDBTransaction::requestGetValue");
+ ASSERT(currentThread() == m_database->originThreadID());
- abort(IGNORE_EXCEPTION);
+ return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Key, range);
}
-void IDBTransaction::enqueueEvent(PassRefPtr<Event> event)
+Ref<IDBRequest> IDBTransaction::requestIndexRecord(ExecState& state, IDBIndex& index, IndexedDB::IndexRecordType type, const IDBKeyRangeData& range)
{
- ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to enqueue an event of type %s.", event->type().string().utf8().data());
- if (m_contextStopped || !scriptExecutionContext())
+ LOG(IndexedDB, "IDBTransaction::requestGetValue");
+ ASSERT(isActive());
+ ASSERT(!range.isNull);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::createIndexGet(*scriptExecutionContext(), index, type, *this);
+ addRequest(request.get());
+
+ IDBGetRecordData getRecordData = { range, IDBGetRecordDataType::KeyAndValue };
+
+ LOG(IndexedDBOperations, "IDB get index record operation: %s %s", index.info().condensedLoggingString().utf8().data(), getRecordData.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, getRecordData));
+
+ return request;
+}
+
+void IDBTransaction::getRecordOnServer(IDBClient::TransactionOperation& operation, const IDBGetRecordData& getRecordData)
+{
+ LOG(IndexedDB, "IDBTransaction::getRecordOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().getRecord(operation, getRecordData);
+}
+
+void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didGetRecordOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (resultData.type() == IDBResultType::Error) {
+ completeNoncursorRequest(request, resultData);
return;
+ }
- event->setTarget(this);
- scriptExecutionContext()->eventQueue().enqueueEvent(event);
+ ASSERT(resultData.type() == IDBResultType::GetRecordSuccess);
+
+ bool useResultKey = request.sourceIndexIdentifier() && request.requestedIndexRecordType() == IndexedDB::IndexRecordType::Key;
+ if (!useResultKey)
+ useResultKey = request.requestedObjectStoreRecordType() == IndexedDB::ObjectStoreRecordType::KeyOnly;
+
+ const IDBGetResult& result = resultData.getResult();
+
+ if (useResultKey) {
+ if (!result.keyData().isNull())
+ request.setResult(result.keyData());
+ else
+ request.setResultToUndefined();
+ } else {
+ if (resultData.getResult().value().data().data())
+ request.setResultToStructuredClone(resultData.getResult().value());
+ else
+ request.setResultToUndefined();
+ }
+
+ completeNoncursorRequest(request, resultData);
+}
+
+Ref<IDBRequest> IDBTransaction::requestCount(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
+{
+ LOG(IndexedDB, "IDBTransaction::requestCount (IDBObjectStore)");
+ ASSERT(isActive());
+ ASSERT(!range.isNull);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
+ addRequest(request.get());
+
+ LOG(IndexedDBOperations, "IDB object store count operation: %s, range %s", objectStore.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range));
+
+ return request;
+}
+
+Ref<IDBRequest> IDBTransaction::requestCount(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
+{
+ LOG(IndexedDB, "IDBTransaction::requestCount (IDBIndex)");
+ ASSERT(isActive());
+ ASSERT(!range.isNull);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), index, *this);
+ addRequest(request.get());
+
+ LOG(IndexedDBOperations, "IDB index count operation: %s, range %s", index.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range));
+
+ return request;
+}
+
+void IDBTransaction::getCountOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
+{
+ LOG(IndexedDB, "IDBTransaction::getCountOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().getCount(operation, keyRange);
+}
+
+void IDBTransaction::didGetCountOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didGetCountOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ request.setResult(resultData.resultInteger());
+ completeNoncursorRequest(request, resultData);
}
-IDBDatabaseBackend* IDBTransaction::backendDB() const
+Ref<IDBRequest> IDBTransaction::requestDeleteRecord(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
{
- return db()->backend();
+ LOG(IndexedDB, "IDBTransaction::requestDeleteRecord");
+ ASSERT(isActive());
+ ASSERT(!range.isNull);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
+ addRequest(request.get());
+
+ LOG(IndexedDBOperations, "IDB delete record operation: %s, range %s", objectStore.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didDeleteRecordOnServer, &IDBTransaction::deleteRecordOnServer, range));
+ return request;
+}
+
+void IDBTransaction::deleteRecordOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
+{
+ LOG(IndexedDB, "IDBTransaction::deleteRecordOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().deleteRecord(operation, keyRange);
+}
+
+void IDBTransaction::didDeleteRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didDeleteRecordOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ request.setResultToUndefined();
+ completeNoncursorRequest(request, resultData);
+}
+
+Ref<IDBRequest> IDBTransaction::requestClearObjectStore(ExecState& state, IDBObjectStore& objectStore)
+{
+ LOG(IndexedDB, "IDBTransaction::requestClearObjectStore");
+ ASSERT(isActive());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
+ addRequest(request.get());
+
+ uint64_t objectStoreIdentifier = objectStore.info().identifier();
+
+ LOG(IndexedDBOperations, "IDB clear object store operation: %s", objectStore.info().condensedLoggingString().utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didClearObjectStoreOnServer, &IDBTransaction::clearObjectStoreOnServer, objectStoreIdentifier));
+
+ return request;
+}
+
+void IDBTransaction::clearObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier)
+{
+ LOG(IndexedDB, "IDBTransaction::clearObjectStoreOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().clearObjectStore(operation, objectStoreIdentifier);
+}
+
+void IDBTransaction::didClearObjectStoreOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didClearObjectStoreOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ request.setResultToUndefined();
+ completeNoncursorRequest(request, resultData);
+}
+
+Ref<IDBRequest> IDBTransaction::requestPutOrAdd(ExecState& state, IDBObjectStore& objectStore, IDBKey* key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
+{
+ LOG(IndexedDB, "IDBTransaction::requestPutOrAdd");
+ ASSERT(isActive());
+ ASSERT(!isReadOnly());
+ ASSERT(objectStore.info().autoIncrement() || key);
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
+
+ auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
+ addRequest(request.get());
+
+ LOG(IndexedDBOperations, "IDB putOrAdd operation: %s key: %s", objectStore.info().condensedLoggingString().utf8().data(), key ? key->loggingString().utf8().data() : "<null key>");
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode));
+
+ return request;
+}
+
+void IDBTransaction::putOrAddOnServer(IDBClient::TransactionOperation& operation, RefPtr<IDBKey> key, RefPtr<SerializedScriptValue> value, const IndexedDB::ObjectStoreOverwriteMode& overwriteMode)
+{
+ LOG(IndexedDB, "IDBTransaction::putOrAddOnServer");
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(!isReadOnly());
+ ASSERT(value);
+
+ if (!value->hasBlobURLs()) {
+ m_database->connectionProxy().putOrAdd(operation, key.get(), *value, overwriteMode);
+ return;
+ }
+
+ // Due to current limitations on our ability to post tasks back to a worker thread,
+ // workers currently write blobs to disk synchronously.
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=157958 - Make this asynchronous after refactoring allows it.
+ if (!isMainThread()) {
+ auto idbValue = value->writeBlobsToDiskForIndexedDBSynchronously();
+ if (idbValue.data().data())
+ m_database->connectionProxy().putOrAdd(operation, key.get(), idbValue, overwriteMode);
+ else {
+ // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
+ // In that case, we cannot successfully store this record, so we callback with an error.
+ RefPtr<IDBClient::TransactionOperation> protectedOperation(&operation);
+ auto result = IDBResultData::error(operation.identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") });
+ scriptExecutionContext()->postTask([protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)](ScriptExecutionContext&) {
+ protectedOperation->doComplete(result);
+ });
+ }
+ return;
+ }
+
+ // Since this request won't actually go to the server until the blob writes are complete,
+ // stop future requests from going to the server ahead of it.
+ operation.setNextRequestCanGoToServer(false);
+
+ value->writeBlobsToDiskForIndexedDB([protectedThis = makeRef(*this), this, protectedOperation = Ref<IDBClient::TransactionOperation>(operation), keyData = IDBKeyData(key.get()).isolatedCopy(), overwriteMode](const IDBValue& idbValue) mutable {
+ ASSERT(currentThread() == originThreadID());
+ ASSERT(isMainThread());
+ if (idbValue.data().data()) {
+ m_database->connectionProxy().putOrAdd(protectedOperation.get(), WTFMove(keyData), idbValue, overwriteMode);
+ return;
+ }
+
+ // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
+ // In that case, we cannot successfully store this record, so we callback with an error.
+ auto result = IDBResultData::error(protectedOperation->identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") });
+ callOnMainThread([protectedThis = WTFMove(protectedThis), protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)]() mutable {
+ protectedOperation->doComplete(result);
+ });
+ });
+}
+
+void IDBTransaction::didPutOrAddOnServer(IDBRequest& request, const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didPutOrAddOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (auto* result = resultData.resultKey())
+ request.setResult(*result);
+ else
+ request.setResultToUndefined();
+ completeNoncursorRequest(request, resultData);
+}
+
+void IDBTransaction::deleteObjectStore(const String& objectStoreName)
+{
+ LOG(IndexedDB, "IDBTransaction::deleteObjectStore");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(isVersionChange());
+
+ Locker<Lock> locker(m_referencedObjectStoreLock);
+
+ if (auto objectStore = m_referencedObjectStores.take(objectStoreName)) {
+ objectStore->markAsDeleted();
+ auto identifier = objectStore->info().identifier();
+ m_deletedObjectStores.set(identifier, WTFMove(objectStore));
+ }
+
+ LOG(IndexedDBOperations, "IDB delete object store operation: %s", objectStoreName.utf8().data());
+ scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteObjectStoreOnServer, &IDBTransaction::deleteObjectStoreOnServer, objectStoreName));
+}
+
+void IDBTransaction::deleteObjectStoreOnServer(IDBClient::TransactionOperation& operation, const String& objectStoreName)
+{
+ LOG(IndexedDB, "IDBTransaction::deleteObjectStoreOnServer");
+ ASSERT(isVersionChange());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().deleteObjectStore(operation, objectStoreName);
}
+void IDBTransaction::didDeleteObjectStoreOnServer(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didDeleteObjectStoreOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess || resultData.type() == IDBResultType::Error);
}
+void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName)
+{
+ LOG(IndexedDB, "IDBTransaction::deleteIndex");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(isVersionChange());
+
+ LOG(IndexedDBOperations, "IDB delete index operation: %s (%" PRIu64 ")", indexName.utf8().data(), objectStoreIdentifier);
+ scheduleOperation(IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteIndexOnServer, &IDBTransaction::deleteIndexOnServer, objectStoreIdentifier, indexName));
+}
+
+void IDBTransaction::deleteIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName)
+{
+ LOG(IndexedDB, "IDBTransaction::deleteIndexOnServer");
+ ASSERT(isVersionChange());
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().deleteIndex(operation, objectStoreIdentifier, indexName);
+}
+
+void IDBTransaction::didDeleteIndexOnServer(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBTransaction::didDeleteIndexOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess || resultData.type() == IDBResultType::Error);
+}
+
+void IDBTransaction::operationCompletedOnClient(IDBClient::TransactionOperation& operation)
+{
+ LOG(IndexedDB, "IDBTransaction::operationCompletedOnClient");
+
+ ASSERT(currentThread() == m_database->originThreadID());
+ ASSERT(currentThread() == operation.originThreadID());
+ ASSERT(m_transactionOperationMap.get(operation.identifier()) == &operation);
+ ASSERT(m_transactionOperationsInProgressQueue.first() == &operation);
+
+ m_transactionOperationMap.remove(operation.identifier());
+ m_transactionOperationsInProgressQueue.removeFirst();
+
+ schedulePendingOperationTimer();
+}
+
+void IDBTransaction::establishOnServer()
+{
+ LOG(IndexedDB, "IDBTransaction::establishOnServer");
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ m_database->connectionProxy().establishTransaction(*this);
+}
+
+void IDBTransaction::activate()
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (isFinishedOrFinishing())
+ return;
+
+ m_state = IndexedDB::TransactionState::Active;
+}
+
+void IDBTransaction::deactivate()
+{
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ if (m_state == IndexedDB::TransactionState::Active)
+ m_state = IndexedDB::TransactionState::Inactive;
+
+ schedulePendingOperationTimer();
+}
+
+void IDBTransaction::connectionClosedFromServer(const IDBError& error)
+{
+ LOG(IndexedDB, "IDBTransaction::connectionClosedFromServer - %s", error.message().utf8().data());
+
+ m_state = IndexedDB::TransactionState::Aborting;
+
+ abortInProgressOperations(error);
+
+ Vector<RefPtr<IDBClient::TransactionOperation>> operations;
+ copyValuesToVector(m_transactionOperationMap, operations);
+
+ for (auto& operation : operations) {
+ m_currentlyCompletingRequest = nullptr;
+ m_transactionOperationsInProgressQueue.append(operation.get());
+ ASSERT(m_transactionOperationsInProgressQueue.first() == operation.get());
+ operation->doComplete(IDBResultData::error(operation->identifier(), error));
+ }
+
+ connectionProxy().forgetActiveOperations(operations);
+
+ m_pendingTransactionOperationQueue.clear();
+ m_abortQueue.clear();
+ m_transactionOperationMap.clear();
+
+ m_idbError = error;
+ m_domError = error.toDOMError();
+ fireOnAbort();
+}
+
+void IDBTransaction::visitReferencedObjectStores(JSC::SlotVisitor& visitor) const
+{
+ Locker<Lock> locker(m_referencedObjectStoreLock);
+ for (auto& objectStore : m_referencedObjectStores.values())
+ visitor.addOpaqueRoot(objectStore.get());
+ for (auto& objectStore : m_deletedObjectStores.values())
+ visitor.addOpaqueRoot(objectStore.get());
+}
+
+} // namespace WebCore
+
#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransaction.h b/Source/WebCore/Modules/indexeddb/IDBTransaction.h
index f6e4ad98c..af96c4cd9 100644
--- a/Source/WebCore/Modules/indexeddb/IDBTransaction.h
+++ b/Source/WebCore/Modules/indexeddb/IDBTransaction.h
@@ -1,174 +1,285 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBTransaction_h
-#define IDBTransaction_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
-#include "ActiveDOMObject.h"
-#include "DOMError.h"
-#include "Event.h"
-#include "EventListener.h"
-#include "EventNames.h"
#include "EventTarget.h"
-#include "IDBDatabaseMetadata.h"
+#include "IDBActiveDOMObject.h"
+#include "IDBError.h"
+#include "IDBGetAllRecordsData.h"
+#include "IDBGetRecordData.h"
+#include "IDBKeyRangeData.h"
+#include "IDBOpenDBRequest.h"
+#include "IDBTransactionInfo.h"
+#include "IDBTransactionMode.h"
#include "IndexedDB.h"
-#include "ScriptWrappable.h"
-#include <wtf/HashSet.h>
-#include <wtf/RefCounted.h>
+#include "Timer.h"
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
namespace WebCore {
+class DOMError;
+class DOMStringList;
class IDBCursor;
+class IDBCursorInfo;
class IDBDatabase;
-class IDBDatabaseBackend;
-class IDBDatabaseError;
+class IDBIndex;
+class IDBIndexInfo;
+class IDBKey;
+class IDBKeyData;
class IDBObjectStore;
-class IDBOpenDBRequest;
-struct IDBObjectStoreMetadata;
+class IDBObjectStoreInfo;
+class IDBResultData;
+class SerializedScriptValue;
-class IDBTransaction final : public ScriptWrappable, public RefCounted<IDBTransaction>, public EventTargetWithInlineData, public ActiveDOMObject {
-public:
- static PassRefPtr<IDBTransaction> create(ScriptExecutionContext*, int64_t, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode, IDBDatabase*);
- static PassRefPtr<IDBTransaction> create(ScriptExecutionContext*, int64_t, IDBDatabase*, IDBOpenDBRequest*, const IDBDatabaseMetadata& previousMetadata);
- virtual ~IDBTransaction();
-
- static const AtomicString& modeReadOnly();
- static const AtomicString& modeReadWrite();
- static const AtomicString& modeVersionChange();
- static const AtomicString& modeReadOnlyLegacy();
- static const AtomicString& modeReadWriteLegacy();
-
- static IndexedDB::TransactionMode stringToMode(const String&, ExceptionCode&);
- static const AtomicString& modeToString(IndexedDB::TransactionMode);
-
- IDBDatabaseBackend* backendDB() const;
-
- int64_t id() const { return m_id; }
- bool isActive() const { return m_state == Active; }
- bool isFinished() const { return m_state == Finished; }
- bool isReadOnly() const { return m_mode == IndexedDB::TransactionMode::ReadOnly; }
- bool isVersionChange() const { return m_mode == IndexedDB::TransactionMode::VersionChange; }
-
- // Implement the IDBTransaction IDL
- const String& mode() const;
- IDBDatabase* db() const { return m_database.get(); }
- PassRefPtr<DOMError> error() const { return m_error; }
- PassRefPtr<IDBObjectStore> objectStore(const String& name, ExceptionCode&);
- void abort(ExceptionCode&);
-
- class OpenCursorNotifier {
- public:
- OpenCursorNotifier(PassRefPtr<IDBTransaction>, IDBCursor*);
- ~OpenCursorNotifier();
- void cursorFinished();
- private:
- RefPtr<IDBTransaction> m_transaction;
- IDBCursor* m_cursor;
- };
-
- void registerRequest(IDBRequest*);
- void unregisterRequest(IDBRequest*);
- void objectStoreCreated(const String&, PassRefPtr<IDBObjectStore>);
- void objectStoreDeleted(const String&);
- void setActive(bool);
- void setError(PassRefPtr<DOMError>, const String& errorMessage);
-
- DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(complete);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
-
- void onAbort(PassRefPtr<IDBDatabaseError>);
- void onComplete();
-
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const override { return IDBTransactionEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); }
+struct IDBIterateCursorData;
+struct IDBKeyRangeData;
+
+namespace IDBClient {
+class IDBConnectionProxy;
+class TransactionOperation;
+}
+class IDBTransaction : public ThreadSafeRefCounted<IDBTransaction>, public EventTargetWithInlineData, public IDBActiveDOMObject {
+public:
+ static Ref<IDBTransaction> create(IDBDatabase&, const IDBTransactionInfo&);
+ static Ref<IDBTransaction> create(IDBDatabase&, const IDBTransactionInfo&, IDBOpenDBRequest&);
+
+ ~IDBTransaction() final;
+
+ // IDBTransaction IDL
+ Ref<DOMStringList> objectStoreNames() const;
+ IDBTransactionMode mode() const { return m_info.mode(); }
+ IDBDatabase* db();
+ DOMError* error() const;
+ ExceptionOr<Ref<IDBObjectStore>> objectStore(const String& name);
+ ExceptionOr<void> abort();
+
+ EventTargetInterface eventTargetInterface() const final { return IDBTransactionEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
+ void refEventTarget() final { ThreadSafeRefCounted::ref(); }
+ void derefEventTarget() final { ThreadSafeRefCounted::deref(); }
using EventTarget::dispatchEvent;
- virtual bool dispatchEvent(PassRefPtr<Event>) override;
+ bool dispatchEvent(Event&) final;
+
+ using ThreadSafeRefCounted<IDBTransaction>::ref;
+ using ThreadSafeRefCounted<IDBTransaction>::deref;
+
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
+ bool hasPendingActivity() const final;
+ void stop() final;
+
+ const IDBTransactionInfo& info() const { return m_info; }
+ IDBDatabase& database() { return m_database.get(); }
+ const IDBDatabase& database() const { return m_database.get(); }
+ IDBDatabaseInfo* originalDatabaseInfo() const { return m_info.originalDatabaseInfo(); }
+
+ void didStart(const IDBError&);
+ void didAbort(const IDBError&);
+ void didCommit(const IDBError&);
+
+ bool isVersionChange() const { return mode() == IDBTransactionMode::Versionchange; }
+ bool isReadOnly() const { return mode() == IDBTransactionMode::Readonly; }
+ bool isActive() const;
+
+ Ref<IDBObjectStore> createObjectStore(const IDBObjectStoreInfo&);
+ void renameObjectStore(IDBObjectStore&, const String& newName);
+ std::unique_ptr<IDBIndex> createIndex(IDBObjectStore&, const IDBIndexInfo&);
+ void renameIndex(IDBIndex&, const String& newName);
+
+ Ref<IDBRequest> requestPutOrAdd(JSC::ExecState&, IDBObjectStore&, IDBKey*, SerializedScriptValue&, IndexedDB::ObjectStoreOverwriteMode);
+ Ref<IDBRequest> requestGetRecord(JSC::ExecState&, IDBObjectStore&, const IDBGetRecordData&);
+ Ref<IDBRequest> requestGetAllObjectStoreRecords(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&, IndexedDB::GetAllType, std::optional<uint32_t> count);
+ Ref<IDBRequest> requestGetAllIndexRecords(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&, IndexedDB::GetAllType, std::optional<uint32_t> count);
+ Ref<IDBRequest> requestDeleteRecord(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestClearObjectStore(JSC::ExecState&, IDBObjectStore&);
+ Ref<IDBRequest> requestCount(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestCount(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestGetValue(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestGetKey(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestOpenCursor(JSC::ExecState&, IDBObjectStore&, const IDBCursorInfo&);
+ Ref<IDBRequest> requestOpenCursor(JSC::ExecState&, IDBIndex&, const IDBCursorInfo&);
+ void iterateCursor(IDBCursor&, const IDBIterateCursorData&);
+
+ void deleteObjectStore(const String& objectStoreName);
+ void deleteIndex(uint64_t objectStoreIdentifier, const String& indexName);
- // ActiveDOMObject
- virtual bool hasPendingActivity() const override;
+ void addRequest(IDBRequest&);
+ void removeRequest(IDBRequest&);
- using RefCounted<IDBTransaction>::ref;
- using RefCounted<IDBTransaction>::deref;
+ void abortDueToFailedRequest(DOMError&);
+
+ void activate();
+ void deactivate();
+
+ void operationCompletedOnServer(const IDBResultData&, IDBClient::TransactionOperation&);
+ void operationCompletedOnClient(IDBClient::TransactionOperation&);
+
+ void finishedDispatchEventForRequest(IDBRequest&);
+
+ bool isFinishedOrFinishing() const;
+ bool isFinished() const { return m_state == IndexedDB::TransactionState::Finished; }
+
+ IDBClient::IDBConnectionProxy& connectionProxy();
+
+ void connectionClosedFromServer(const IDBError&);
+
+ void visitReferencedObjectStores(JSC::SlotVisitor&) const;
private:
- IDBTransaction(ScriptExecutionContext*, int64_t, const Vector<String>&, IndexedDB::TransactionMode, IDBDatabase*, IDBOpenDBRequest*, const IDBDatabaseMetadata&);
+ IDBTransaction(IDBDatabase&, const IDBTransactionInfo&, IDBOpenDBRequest*);
+
+ void commit();
+
+ void internalAbort();
+ void notifyDidAbort(const IDBError&);
+ void finishAbortOrCommit();
+ void abortInProgressOperations(const IDBError&);
+
+ void scheduleOperation(RefPtr<IDBClient::TransactionOperation>&&);
+ void pendingOperationTimerFired();
+ void completedOperationTimerFired();
+
+ void fireOnComplete();
+ void fireOnAbort();
+ void enqueueEvent(Ref<Event>&&);
+
+ Ref<IDBRequest> requestIndexRecord(JSC::ExecState&, IDBIndex&, IndexedDB::IndexRecordType, const IDBKeyRangeData&);
- void enqueueEvent(PassRefPtr<Event>);
- void closeOpenCursors();
+ void commitOnServer(IDBClient::TransactionOperation&);
+ void abortOnServerAndCancelRequests(IDBClient::TransactionOperation&);
- void registerOpenCursor(IDBCursor*);
- void unregisterOpenCursor(IDBCursor*);
+ void createObjectStoreOnServer(IDBClient::TransactionOperation&, const IDBObjectStoreInfo&);
+ void didCreateObjectStoreOnServer(const IDBResultData&);
- // ActiveDOMObject
- virtual bool canSuspend() const override;
- virtual void stop() override;
+ void renameObjectStoreOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier, const String& newName);
+ void didRenameObjectStoreOnServer(const IDBResultData&);
- // EventTarget
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void createIndexOnServer(IDBClient::TransactionOperation&, const IDBIndexInfo&);
+ void didCreateIndexOnServer(const IDBResultData&);
- enum State {
- Inactive, // Created or started, but not in an event callback
- Active, // Created or started, in creation scope or an event callback
- Finishing, // In the process of aborting or completing.
- Finished, // No more events will fire and no new requests may be filed.
- };
+ void renameIndexOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier, const uint64_t& indexIdentifier, const String& newName);
+ void didRenameIndexOnServer(const IDBResultData&);
- int64_t m_id;
- RefPtr<IDBDatabase> m_database;
- const Vector<String> m_objectStoreNames;
- IDBOpenDBRequest* m_openDBRequest;
- const IndexedDB::TransactionMode m_mode;
- State m_state;
- bool m_hasPendingActivity;
- bool m_contextStopped;
- RefPtr<DOMError> m_error;
- String m_errorMessage;
+ void clearObjectStoreOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier);
+ void didClearObjectStoreOnServer(IDBRequest&, const IDBResultData&);
- ListHashSet<RefPtr<IDBRequest>> m_requestList;
+ void putOrAddOnServer(IDBClient::TransactionOperation&, RefPtr<IDBKey>, RefPtr<SerializedScriptValue>, const IndexedDB::ObjectStoreOverwriteMode&);
+ void didPutOrAddOnServer(IDBRequest&, const IDBResultData&);
- typedef HashMap<String, RefPtr<IDBObjectStore>> IDBObjectStoreMap;
- IDBObjectStoreMap m_objectStoreMap;
+ void getRecordOnServer(IDBClient::TransactionOperation&, const IDBGetRecordData&);
+ void didGetRecordOnServer(IDBRequest&, const IDBResultData&);
- typedef HashSet<RefPtr<IDBObjectStore>> IDBObjectStoreSet;
- IDBObjectStoreSet m_deletedObjectStores;
+ void getAllRecordsOnServer(IDBClient::TransactionOperation&, const IDBGetAllRecordsData&);
+ void didGetAllRecordsOnServer(IDBRequest&, const IDBResultData&);
- typedef HashMap<RefPtr<IDBObjectStore>, IDBObjectStoreMetadata> IDBObjectStoreMetadataMap;
- IDBObjectStoreMetadataMap m_objectStoreCleanupMap;
- IDBDatabaseMetadata m_previousMetadata;
+ void getCountOnServer(IDBClient::TransactionOperation&, const IDBKeyRangeData&);
+ void didGetCountOnServer(IDBRequest&, const IDBResultData&);
- HashSet<IDBCursor*> m_openCursors;
+ void deleteRecordOnServer(IDBClient::TransactionOperation&, const IDBKeyRangeData&);
+ void didDeleteRecordOnServer(IDBRequest&, const IDBResultData&);
+
+ void deleteObjectStoreOnServer(IDBClient::TransactionOperation&, const String& objectStoreName);
+ void didDeleteObjectStoreOnServer(const IDBResultData&);
+
+ void deleteIndexOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier, const String& indexName);
+ void didDeleteIndexOnServer(const IDBResultData&);
+
+ Ref<IDBRequest> doRequestOpenCursor(JSC::ExecState&, Ref<IDBCursor>&&);
+ void openCursorOnServer(IDBClient::TransactionOperation&, const IDBCursorInfo&);
+ void didOpenCursorOnServer(IDBRequest&, const IDBResultData&);
+
+ void iterateCursorOnServer(IDBClient::TransactionOperation&, const IDBIterateCursorData&);
+ void didIterateCursorOnServer(IDBRequest&, const IDBResultData&);
+
+ void transitionedToFinishing(IndexedDB::TransactionState);
+
+ void establishOnServer();
+
+ void completeNoncursorRequest(IDBRequest&, const IDBResultData&);
+ void completeCursorRequest(IDBRequest&, const IDBResultData&);
+
+ void schedulePendingOperationTimer();
+ void scheduleCompletedOperationTimer();
+
+ Ref<IDBDatabase> m_database;
+ IDBTransactionInfo m_info;
+
+ IndexedDB::TransactionState m_state { IndexedDB::TransactionState::Inactive };
+ bool m_startedOnServer { false };
+
+ IDBError m_idbError;
+ RefPtr<DOMError> m_domError;
+
+ Timer m_pendingOperationTimer;
+ Timer m_completedOperationTimer;
+ std::unique_ptr<Timer> m_activationTimer;
+
+ RefPtr<IDBOpenDBRequest> m_openDBRequest;
+
+ Deque<RefPtr<IDBClient::TransactionOperation>> m_pendingTransactionOperationQueue;
+ Deque<IDBClient::TransactionOperation*> m_transactionOperationsInProgressQueue;
+ Deque<std::pair<RefPtr<IDBClient::TransactionOperation>, IDBResultData>> m_completedOnServerQueue;
+ Deque<RefPtr<IDBClient::TransactionOperation>> m_abortQueue;
+
+ HashMap<IDBResourceIdentifier, RefPtr<IDBClient::TransactionOperation>> m_transactionOperationMap;
+
+ mutable Lock m_referencedObjectStoreLock;
+ HashMap<String, std::unique_ptr<IDBObjectStore>> m_referencedObjectStores;
+ HashMap<uint64_t, std::unique_ptr<IDBObjectStore>> m_deletedObjectStores;
+
+ HashSet<RefPtr<IDBRequest>> m_openRequests;
+ RefPtr<IDBRequest> m_currentlyCompletingRequest;
+
+ bool m_contextStopped { false };
+};
+
+class TransactionActivator {
+ WTF_MAKE_NONCOPYABLE(TransactionActivator);
+public:
+ TransactionActivator(IDBTransaction* transaction)
+ : m_transaction(transaction)
+ {
+ if (m_transaction)
+ m_transaction->activate();
+ }
+
+ ~TransactionActivator()
+ {
+ if (m_transaction)
+ m_transaction->deactivate();
+ }
+
+private:
+ IDBTransaction* m_transaction;
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBTransaction_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransaction.idl b/Source/WebCore/Modules/indexeddb/IDBTransaction.idl
index b770f892f..769e919b4 100644
--- a/Source/WebCore/Modules/indexeddb/IDBTransaction.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBTransaction.idl
@@ -25,21 +25,20 @@
*/
[
- Conditional=INDEXED_DATABASE,
ActiveDOMObject,
- EventTarget,
- JSNoStaticTables,
- JSGenerateToJSObject,
- JSGenerateToNativeObject,
+ Conditional=INDEXED_DATABASE,
+ JSCustomMarkFunction,
+ SkipVTableValidation,
] interface IDBTransaction : EventTarget {
- readonly attribute DOMString mode;
+ readonly attribute DOMStringList objectStoreNames;
+ readonly attribute IDBTransactionMode mode;
readonly attribute IDBDatabase db;
readonly attribute DOMError error;
- [RaisesException] IDBObjectStore objectStore (DOMString name);
- [RaisesException] void abort ();
+ [MayThrowException] IDBObjectStore objectStore(DOMString name);
+ [MayThrowException] void abort();
- attribute EventListener onabort;
- attribute EventListener oncomplete;
- attribute EventListener onerror;
+ attribute EventHandler onabort;
+ attribute EventHandler oncomplete;
+ attribute EventHandler onerror;
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp b/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp
deleted file mode 100644
index bfb808815..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp
+++ /dev/null
@@ -1,359 +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.
- *
- * 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 "IDBTransactionBackend.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBCursorBackend.h"
-#include "IDBDatabaseBackend.h"
-#include "IDBDatabaseCallbacks.h"
-#include "IDBDatabaseException.h"
-#include "IDBFactoryBackendInterface.h"
-#include "IDBKeyRange.h"
-#include "IDBServerConnection.h"
-#include "IDBTransactionBackendOperations.h"
-#include "IDBTransactionCoordinator.h"
-#include "Logging.h"
-
-namespace WebCore {
-
-PassRefPtr<IDBTransactionBackend> IDBTransactionBackend::create(IDBDatabaseBackend* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
-{
- HashSet<int64_t> objectStoreHashSet;
- for (size_t i = 0; i < objectStoreIds.size(); ++i)
- objectStoreHashSet.add(objectStoreIds[i]);
-
- return adoptRef(new IDBTransactionBackend(databaseBackend, id, callbacks, objectStoreHashSet, mode));
-}
-
-IDBTransactionBackend::IDBTransactionBackend(IDBDatabaseBackend* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const HashSet<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
- : m_objectStoreIds(objectStoreIds)
- , m_mode(mode)
- , m_state(Unopened)
- , m_commitPending(false)
- , m_callbacks(callbacks)
- , m_database(databaseBackend)
- , m_taskTimer(this, &IDBTransactionBackend::taskTimerFired)
- , m_pendingPreemptiveEvents(0)
- , m_id(id)
-{
- // We pass a reference of this object before it can be adopted.
- relaxAdoptionRequirement();
-
- m_database->transactionCoordinator()->didCreateTransaction(this);
-
- RefPtr<IDBTransactionBackend> backend(this);
- m_database->serverConnection().openTransaction(id, objectStoreIds, mode, [backend](bool success) {
- if (!success) {
- callOnMainThread([backend]() {
- backend->abort();
- });
- return;
- }
-
- backend->m_state = Unused;
- if (backend->hasPendingTasks())
- backend->start();
- });
-}
-
-IDBTransactionBackend::~IDBTransactionBackend()
-{
- // It shouldn't be possible for this object to get deleted until it's either complete or aborted.
- ASSERT(m_state == Finished);
-}
-
-void IDBTransactionBackend::scheduleTask(IDBDatabaseBackend::TaskType type, PassRefPtr<IDBOperation> task, PassRefPtr<IDBSynchronousOperation> abortTask)
-{
- if (m_state == Finished)
- return;
-
- if (type == IDBDatabaseBackend::NormalTask)
- m_taskQueue.append(task);
- else
- m_preemptiveTaskQueue.append(task);
-
- if (abortTask)
- m_abortTaskQueue.prepend(abortTask);
-
- if (m_state == Unopened)
- return;
-
- if (m_state == Unused)
- start();
- else if (m_state == Running && !m_taskTimer.isActive())
- m_taskTimer.startOneShot(0);
-}
-
-void IDBTransactionBackend::abort()
-{
- abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error (unknown cause)"));
-}
-
-void IDBTransactionBackend::abort(PassRefPtr<IDBDatabaseError> error)
-{
- LOG(StorageAPI, "IDBTransactionBackend::abort");
- if (m_state == Finished)
- return;
-
- bool wasRunning = m_state == Running;
-
- // The last reference to this object may be released while performing the
- // abort steps below. We therefore take a self reference to keep ourselves
- // alive while executing this method.
- Ref<IDBTransactionBackend> protect(*this);
-
- m_state = Finished;
- m_taskTimer.stop();
-
- if (wasRunning)
- m_database->serverConnection().rollbackTransaction(m_id, []() { });
-
- // Run the abort tasks, if any.
- while (!m_abortTaskQueue.isEmpty()) {
- RefPtr<IDBSynchronousOperation> task(m_abortTaskQueue.takeFirst());
- task->perform();
- }
-
- // Backing store resources (held via cursors) must be released before script callbacks
- // are fired, as the script callbacks may release references and allow the backing store
- // itself to be released, and order is critical.
- closeOpenCursors();
-
- m_database->serverConnection().resetTransaction(m_id, []() { });
-
- // Transactions must also be marked as completed before the front-end is notified, as
- // the transaction completion unblocks operations like closing connections.
- m_database->transactionCoordinator()->didFinishTransaction(this);
- ASSERT(!m_database->transactionCoordinator()->isActive(this));
- m_database->transactionFinished(this);
-
- if (m_callbacks)
- m_callbacks->onAbort(id(), error);
-
- m_database->transactionFinishedAndAbortFired(this);
-
- m_database = 0;
-}
-
-bool IDBTransactionBackend::isTaskQueueEmpty() const
-{
- return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty();
-}
-
-bool IDBTransactionBackend::hasPendingTasks() const
-{
- return m_pendingPreemptiveEvents || !isTaskQueueEmpty();
-}
-
-void IDBTransactionBackend::registerOpenCursor(IDBCursorBackend* cursor)
-{
- m_openCursors.add(cursor);
-}
-
-void IDBTransactionBackend::unregisterOpenCursor(IDBCursorBackend* cursor)
-{
- m_openCursors.remove(cursor);
-}
-
-void IDBTransactionBackend::run()
-{
- // TransactionCoordinator has started this transaction. Schedule a timer
- // to process the first task.
- ASSERT(m_state == StartPending || m_state == Running);
- ASSERT(!m_taskTimer.isActive());
-
- m_taskTimer.startOneShot(0);
-}
-
-void IDBTransactionBackend::start()
-{
- ASSERT(m_state == Unused);
-
- m_state = StartPending;
- m_database->transactionCoordinator()->didStartTransaction(this);
- m_database->transactionStarted(this);
-}
-
-void IDBTransactionBackend::commit()
-{
- LOG(StorageAPI, "IDBTransactionBackend::commit (Transaction %lli)", static_cast<long long>(m_id));
-
- // In multiprocess ports, front-end may have requested a commit but an abort has already
- // been initiated asynchronously by the back-end.
- if (m_state == Finished)
- return;
-
- ASSERT(m_state == Unused || m_state == Running);
- m_commitPending = true;
-
- // Front-end has requested a commit, but there may be tasks like createIndex which
- // are considered synchronous by the front-end but are processed asynchronously.
- if (hasPendingTasks()) {
- LOG(StorageAPI, "IDBTransactionBackend::commit - Not committing now, transaction still has pending tasks (Transaction %lli)", static_cast<long long>(m_id));
- return;
- }
-
- // The last reference to this object may be released while performing the
- // commit steps below. We therefore take a self reference to keep ourselves
- // alive while executing this method.
- RefPtr<IDBTransactionBackend> backend(this);
-
- bool unused = m_state == Unused;
- m_state = Finished;
-
- bool committed = unused;
-
- m_database->serverConnection().commitTransaction(m_id, [backend, this, committed, unused](bool success) mutable {
- committed |= success;
-
- // Backing store resources (held via cursors) must be released before script callbacks
- // are fired, as the script callbacks may release references and allow the backing store
- // itself to be released, and order is critical.
- closeOpenCursors();
-
- m_database->serverConnection().resetTransaction(m_id, []() { });
-
- // Transactions must also be marked as completed before the front-end is notified, as
- // the transaction completion unblocks operations like closing connections.
- if (!unused)
- m_database->transactionCoordinator()->didFinishTransaction(this);
- m_database->transactionFinished(this);
-
- if (committed) {
- m_callbacks->onComplete(id());
- m_database->transactionFinishedAndCompleteFired(this);
- } else {
- m_callbacks->onAbort(id(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error committing transaction."));
- m_database->transactionFinishedAndAbortFired(this);
- }
-
- m_database = 0;
- });
-}
-
-void IDBTransactionBackend::taskTimerFired(Timer<IDBTransactionBackend>&)
-{
- LOG(StorageAPI, "IDBTransactionBackend::taskTimerFired");
-
- if (m_state == StartPending) {
- m_database->serverConnection().beginTransaction(m_id, []() { });
- m_state = Running;
- }
-
- // The last reference to this object may be released while performing a task.
- // Take a self reference to keep this object alive so that tasks can
- // successfully make their completion callbacks.
- RefPtr<IDBTransactionBackend> self(this);
-
- TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
- if (!taskQueue->isEmpty() && m_state != Finished) {
- ASSERT(m_state == Running);
- RefPtr<IDBOperation> task(taskQueue->takeFirst());
- task->perform([self, this, task]() {
- m_taskTimer.startOneShot(0);
- });
-
- return;
- }
-
- // If there are no pending tasks, we haven't already committed/aborted,
- // and the front-end requested a commit, it is now safe to do so.
- if (!hasPendingTasks() && m_state != Finished && m_commitPending)
- commit();
-}
-
-void IDBTransactionBackend::closeOpenCursors()
-{
- for (HashSet<IDBCursorBackend*>::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i)
- (*i)->close();
- m_openCursors.clear();
-}
-
-void IDBTransactionBackend::scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
-{
- scheduleTask(CreateObjectStoreOperation::create(this, objectStoreMetadata), CreateObjectStoreAbortOperation::create(this, objectStoreMetadata.id));
-}
-
-void IDBTransactionBackend::scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
-{
- scheduleTask(DeleteObjectStoreOperation::create(this, objectStoreMetadata), DeleteObjectStoreAbortOperation::create(this, objectStoreMetadata));
-}
-
-void IDBTransactionBackend::scheduleVersionChangeOperation(int64_t requestedVersion, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, const IDBDatabaseMetadata& metadata)
-{
- scheduleTask(IDBDatabaseBackend::VersionChangeOperation::create(this, requestedVersion, callbacks, databaseCallbacks), IDBDatabaseBackend::VersionChangeAbortOperation::create(this, String::number(metadata.version), metadata.version));
-}
-
-void IDBTransactionBackend::scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
-{
- scheduleTask(CreateIndexOperation::create(this, objectStoreId, indexMetadata), CreateIndexAbortOperation::create(this, objectStoreId, indexMetadata.id));
-}
-
-void IDBTransactionBackend::scheduleDeleteIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
-{
- scheduleTask(DeleteIndexOperation::create(this, objectStoreId, indexMetadata), DeleteIndexAbortOperation::create(this, objectStoreId, indexMetadata));
-}
-
-void IDBTransactionBackend::scheduleGetOperation(const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(GetOperation::create(this, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks));
-}
-
-void IDBTransactionBackend::schedulePutOperation(const IDBObjectStoreMetadata& objectStoreMetadata, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
-{
- scheduleTask(PutOperation::create(this, objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys));
-}
-
-void IDBTransactionBackend::scheduleSetIndexesReadyOperation(size_t indexCount)
-{
- scheduleTask(IDBDatabaseBackend::PreemptiveTask, SetIndexesReadyOperation::create(this, indexCount));
-}
-
-void IDBTransactionBackend::scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(OpenCursorOperation::create(this, objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
-}
-
-void IDBTransactionBackend::scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(CountOperation::create(this, objectStoreId, indexId, keyRange, callbacks));
-}
-
-void IDBTransactionBackend::scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(DeleteRangeOperation::create(this, objectStoreId, keyRange, callbacks));
-}
-
-void IDBTransactionBackend::scheduleClearObjectStoreOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(ClearObjectStoreOperation::create(this, objectStoreId, callbacks));
-}
-
-};
-
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h b/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h
deleted file mode 100644
index bb200dde3..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h
+++ /dev/null
@@ -1,128 +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.
- *
- * 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 IDBTransactionBackend_h
-#define IDBTransactionBackend_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBDatabaseBackend.h"
-#include "IDBDatabaseError.h"
-#include "IDBOperation.h"
-#include "Timer.h"
-#include <wtf/Deque.h>
-#include <wtf/HashSet.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBCursorBackend;
-class IDBDatabaseCallbacks;
-
-class IDBTransactionBackend : public RefCounted<IDBTransactionBackend> {
-public:
- static PassRefPtr<IDBTransactionBackend> create(IDBDatabaseBackend*, int64_t transactionId, PassRefPtr<IDBDatabaseCallbacks>, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode);
- ~IDBTransactionBackend();
-
- void commit();
- void abort();
- void abort(PassRefPtr<IDBDatabaseError>);
-
- void run();
- IndexedDB::TransactionMode mode() const { return m_mode; }
- const HashSet<int64_t>& scope() const { return m_objectStoreIds; }
-
- void scheduleTask(PassRefPtr<IDBOperation> task, PassRefPtr<IDBSynchronousOperation> abortTask = nullptr) { scheduleTask(IDBDatabaseBackend::NormalTask, task, abortTask); }
- void scheduleTask(IDBDatabaseBackend::TaskType, PassRefPtr<IDBOperation>, PassRefPtr<IDBSynchronousOperation> abortTask = nullptr);
-
- void registerOpenCursor(IDBCursorBackend*);
- void unregisterOpenCursor(IDBCursorBackend*);
-
- void addPreemptiveEvent() { m_pendingPreemptiveEvents++; }
- void didCompletePreemptiveEvent() { m_pendingPreemptiveEvents--; ASSERT(m_pendingPreemptiveEvents >= 0); }
-
- IDBDatabaseBackend& database() const { return *m_database; }
-
- void scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata&);
- void scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata&);
- void scheduleVersionChangeOperation(int64_t requestedVersion, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, const IDBDatabaseMetadata&);
- void scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata&);
- void scheduleDeleteIndexOperation(int64_t objectStoreId, const IDBIndexMetadata&);
- void scheduleGetOperation(const IDBDatabaseMetadata&, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, IndexedDB::CursorType, PassRefPtr<IDBCallbacks>);
- void schedulePutOperation(const IDBObjectStoreMetadata&, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey>, IDBDatabaseBackend::PutMode, PassRefPtr<IDBCallbacks>, const Vector<int64_t>& indexIds, const Vector<IndexKeys>&);
- void scheduleSetIndexesReadyOperation(size_t indexCount);
- void scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, IndexedDB::CursorDirection, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, PassRefPtr<IDBCallbacks>);
- void scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void scheduleClearObjectStoreOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks>);
-
- int64_t id() const { return m_id; }
-
-private:
- IDBTransactionBackend(IDBDatabaseBackend*, int64_t id, PassRefPtr<IDBDatabaseCallbacks>, const HashSet<int64_t>& objectStoreIds, IndexedDB::TransactionMode);
-
- enum State {
- Unopened, // Backing store transaction not yet created.
- Unused, // Backing store transaction created, but no tasks yet.
- StartPending, // Enqueued tasks, but backing store transaction not yet started.
- Running, // Backing store transaction started but not yet finished.
- Finished, // Either aborted or committed.
- };
-
- void start();
-
- bool isTaskQueueEmpty() const;
- bool hasPendingTasks() const;
-
- void taskTimerFired(Timer<IDBTransactionBackend>&);
- void closeOpenCursors();
-
- const HashSet<int64_t> m_objectStoreIds;
- const IndexedDB::TransactionMode m_mode;
-
- State m_state;
- bool m_commitPending;
- RefPtr<IDBDatabaseCallbacks> m_callbacks;
- RefPtr<IDBDatabaseBackend> m_database;
-
- typedef Deque<RefPtr<IDBOperation>> TaskQueue;
- TaskQueue m_taskQueue;
- TaskQueue m_preemptiveTaskQueue;
- Deque<RefPtr<IDBSynchronousOperation>> m_abortTaskQueue;
-
- // FIXME: delete the timer once we have threads instead.
- Timer<IDBTransactionBackend> m_taskTimer;
- int m_pendingPreemptiveEvents;
-
- HashSet<IDBCursorBackend*> m_openCursors;
-
- int64_t m_id;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBTransactionBackend_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp b/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp
deleted file mode 100644
index 4b6b0fc46..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBTransactionBackendOperations.h"
-
-#include "IDBCursorBackend.h"
-#include "IDBDatabaseCallbacks.h"
-#include "IDBKeyRange.h"
-#include "IDBRecordIdentifier.h"
-#include "IDBServerConnection.h"
-#include "Logging.h"
-#include <wtf/text/CString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-#define STANDARD_DATABASE_ERROR_CALLBACK std::function<void(PassRefPtr<IDBDatabaseError>)> operationCallback = \
- [operation, completionCallback](PassRefPtr<IDBDatabaseError> error) { \
- if (error) \
- operation->m_transaction->abort(error); \
- completionCallback(); \
- };
-
-namespace WebCore {
-
-void CreateObjectStoreOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "CreateObjectStoreOperation");
-
- RefPtr<CreateObjectStoreOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().createObjectStore(*m_transaction, *this, operationCallback);
-}
-
-void CreateIndexOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "CreateIndexOperation");
-
- RefPtr<CreateIndexOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().createIndex(*m_transaction, *this, operationCallback);
-}
-
-void CreateIndexAbortOperation::perform()
-{
- LOG(StorageAPI, "CreateIndexAbortOperation");
- m_transaction->database().removeIndex(m_objectStoreID, m_indexID);
-}
-
-void DeleteIndexOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "DeleteIndexOperation");
-
- RefPtr<DeleteIndexOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().deleteIndex(*m_transaction, *this, operationCallback);
-}
-
-void DeleteIndexAbortOperation::perform()
-{
- LOG(StorageAPI, "DeleteIndexAbortOperation");
- m_transaction->database().addIndex(m_objectStoreID, m_indexMetadata, IDBIndexMetadata::InvalidId);
-}
-
-void GetOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "GetOperation");
-
- RefPtr<GetOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().get(*m_transaction, *this, [this, operation, operationCallback](const IDBGetResult& result, PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> error = prpError;
-
- if (error)
- m_callbacks->onError(error);
- else {
- if (!result.valueBuffer) {
- if (result.keyData.isNull)
- m_callbacks->onSuccess();
- else
- m_callbacks->onSuccess(result.keyData.maybeCreateIDBKey());
- } else {
- if (!result.keyData.isNull)
- m_callbacks->onSuccess(result.valueBuffer, result.keyData.maybeCreateIDBKey(), result.keyPath);
- else
- m_callbacks->onSuccess(result.valueBuffer.get());
- }
- }
-
- operationCallback(error.release());
- });
-}
-
-void PutOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "PutOperation");
- ASSERT(m_transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
- ASSERT(m_indexIDs.size() == m_indexKeys.size());
-
- RefPtr<PutOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().put(*m_transaction, *this, [this, operation, operationCallback](PassRefPtr<IDBKey> key, PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> error = prpError;
- if (key) {
- ASSERT(!error);
- m_callbacks->onSuccess(key);
- } else {
- ASSERT(error);
- m_callbacks->onError(error);
- }
- operationCallback(error.release());
- });
-}
-
-void SetIndexesReadyOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "SetIndexesReadyOperation");
-
- for (size_t i = 0; i < m_indexCount; ++i)
- m_transaction->didCompletePreemptiveEvent();
-
- callOnMainThread(completionCallback);
-}
-
-void OpenCursorOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "OpenCursorOperation");
-
- RefPtr<OpenCursorOperation> operation(this);
- auto callback = [this, operation, completionCallback](int64_t cursorID, PassRefPtr<IDBDatabaseError>) {
- // FIXME: When the LevelDB port fails to open a backing store cursor it calls onSuccess(nullptr);
- // This seems nonsensical and might have to change soon, breaking them.
- if (!cursorID)
- m_callbacks->onSuccess(static_cast<SharedBuffer*>(0));
- else {
- RefPtr<IDBCursorBackend> cursor = IDBCursorBackend::create(cursorID, m_cursorType, m_taskType, *m_transaction, m_objectStoreID);
- m_callbacks->onSuccess(cursor, cursor->key(), cursor->primaryKey(), cursor->value());
- }
-
- completionCallback();
- };
-
- m_transaction->database().serverConnection().openCursor(*m_transaction, *this, callback);
-}
-
-void CountOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "CountOperation");
-
- RefPtr<CountOperation> operation(this);
- auto callback = [this, operation, completionCallback](int64_t count, PassRefPtr<IDBDatabaseError>) {
- // FIXME: The LevelDB port never had an error condition for the count operation.
- // We probably need to support an error for the count operation, breaking the LevelDB port.
- m_callbacks->onSuccess(count);
-
- completionCallback();
- };
-
- m_transaction->database().serverConnection().count(*m_transaction, *this, callback);
-}
-
-void DeleteRangeOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "DeleteRangeOperation");
-
- RefPtr<DeleteRangeOperation> operation(this);
- auto callback = [this, operation, completionCallback](PassRefPtr<IDBDatabaseError> error) {
- if (error)
- m_callbacks->onError(error);
- else
- m_callbacks->onSuccess();
-
- completionCallback();
- };
-
- m_transaction->database().serverConnection().deleteRange(*m_transaction, *this, callback);
-}
-
-void ClearObjectStoreOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "ClearObjectStoreOperation");
-
- RefPtr<ClearObjectStoreOperation> operation(this);
-
- auto clearCallback = [this, operation, completionCallback](PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> error = prpError;
-
- if (error) {
- m_callbacks->onError(error);
- m_transaction->abort(error.release());
- } else
- m_callbacks->onSuccess();
-
- completionCallback();
- };
-
- m_transaction->database().serverConnection().clearObjectStore(*m_transaction, *this, clearCallback);
-}
-
-void DeleteObjectStoreOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "DeleteObjectStoreOperation");
-
- RefPtr<DeleteObjectStoreOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().deleteObjectStore(*m_transaction, *this, operationCallback);
-}
-
-void IDBDatabaseBackend::VersionChangeOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "VersionChangeOperation");
-
- uint64_t oldVersion = m_transaction->database().metadata().version;
- RefPtr<IDBDatabaseBackend::VersionChangeOperation> operation(this);
- ASSERT(static_cast<uint64_t>(m_version) > oldVersion);
-
- std::function<void(PassRefPtr<IDBDatabaseError>)> operationCallback = [oldVersion, operation, this, completionCallback](PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> error = prpError;
- if (error) {
- m_callbacks->onError(error);
- m_transaction->abort(error);
- } else {
- ASSERT(!m_transaction->database().hasPendingSecondHalfOpen());
- m_transaction->database().setCurrentVersion(m_version);
- m_transaction->database().setPendingSecondHalfOpen(IDBPendingOpenCall::create(*m_callbacks, *m_databaseCallbacks, m_transaction->id(), m_version));
- m_callbacks->onUpgradeNeeded(oldVersion, &m_transaction->database(), m_transaction->database().metadata());
- }
- completionCallback();
- };
-
- m_transaction->database().serverConnection().changeDatabaseVersion(*m_transaction, *this, operationCallback);
-}
-
-void CreateObjectStoreAbortOperation::perform()
-{
- LOG(StorageAPI, "CreateObjectStoreAbortOperation");
- m_transaction->database().removeObjectStore(m_objectStoreID);
-}
-
-void DeleteObjectStoreAbortOperation::perform()
-{
- LOG(StorageAPI, "DeleteObjectStoreAbortOperation");
- m_transaction->database().addObjectStore(m_objectStoreMetadata, IDBObjectStoreMetadata::InvalidId);
-}
-
-void IDBDatabaseBackend::VersionChangeAbortOperation::perform()
-{
- LOG(StorageAPI, "VersionChangeAbortOperation");
- m_transaction->database().setCurrentVersion(m_previousIntVersion);
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h b/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h
deleted file mode 100644
index 964cb85ba..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBTransactionBackendOperations_h
-#define IDBTransactionBackendOperations_h
-
-#include "IDBDatabaseBackend.h"
-#include "IDBOperation.h"
-#include "IDBTransactionBackend.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-class IDBServerConnection;
-
-class CreateObjectStoreOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- {
- return adoptRef(new CreateObjectStoreOperation(transaction, objectStoreMetadata));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- const IDBObjectStoreMetadata& objectStoreMetadata() const { return m_objectStoreMetadata; }
-
-private:
- CreateObjectStoreOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- : m_transaction(transaction)
- , m_objectStoreMetadata(objectStoreMetadata)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const IDBObjectStoreMetadata m_objectStoreMetadata;
-};
-
-class DeleteObjectStoreOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- {
- return adoptRef(new DeleteObjectStoreOperation(transaction, objectStoreMetadata));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- IDBTransactionBackend* transaction() const { return m_transaction.get(); }
- const IDBObjectStoreMetadata& objectStoreMetadata() const { return m_objectStoreMetadata; }
-
-private:
- DeleteObjectStoreOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- : m_transaction(transaction)
- , m_objectStoreMetadata(objectStoreMetadata)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const IDBObjectStoreMetadata m_objectStoreMetadata;
-};
-
-class IDBDatabaseBackend::VersionChangeOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
- {
- return adoptRef(new VersionChangeOperation(transaction, version, callbacks, databaseCallbacks));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- IDBTransactionBackend* transaction() const { return m_transaction.get(); }
- int64_t version() const { return m_version; }
- IDBDatabaseCallbacks* databaseCallbacks() const { return m_databaseCallbacks.get(); }
-
-private:
- VersionChangeOperation(IDBTransactionBackend* transaction, int64_t version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
- : m_transaction(transaction)
- , m_version(version)
- , m_callbacks(callbacks)
- , m_databaseCallbacks(databaseCallbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- int64_t m_version;
- RefPtr<IDBCallbacks> m_callbacks;
- RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
-};
-
-class CreateObjectStoreAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId)
- {
- return adoptRef(new CreateObjectStoreAbortOperation(transaction, objectStoreId));
- }
- virtual void perform() override final;
-private:
- CreateObjectStoreAbortOperation(IDBTransactionBackend* transaction, int64_t objectStoreId)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
-};
-
-class DeleteObjectStoreAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore)
- {
- return adoptRef(new DeleteObjectStoreAbortOperation(transaction, objectStore));
- }
- virtual void perform() override final;
-private:
- DeleteObjectStoreAbortOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- : m_transaction(transaction)
- , m_objectStoreMetadata(objectStoreMetadata)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- IDBObjectStoreMetadata m_objectStoreMetadata;
-};
-
-class IDBDatabaseBackend::VersionChangeAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> create(IDBTransactionBackend* transaction, const String& previousVersion, int64_t previousIntVersion)
- {
- return adoptRef(new VersionChangeAbortOperation(transaction, previousVersion, previousIntVersion));
- }
- virtual void perform() override final;
-private:
- VersionChangeAbortOperation(IDBTransactionBackend* transaction, const String& previousVersion, int64_t previousIntVersion)
- : m_transaction(transaction)
- , m_previousVersion(previousVersion)
- , m_previousIntVersion(previousIntVersion)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- String m_previousVersion;
- int64_t m_previousIntVersion;
-};
-
-class CreateIndexOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- {
- return adoptRef(new CreateIndexOperation(transaction, objectStoreId, indexMetadata));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- int64_t objectStoreID() const { return m_objectStoreID; }
- const IDBIndexMetadata& idbIndexMetadata() const { return m_indexMetadata; }
-
-private:
- CreateIndexOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexMetadata(indexMetadata)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const IDBIndexMetadata m_indexMetadata;
-};
-
-class CreateIndexAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId)
- {
- return adoptRef(new CreateIndexAbortOperation(transaction, objectStoreId, indexId));
- }
- virtual void perform() override final;
-private:
- CreateIndexAbortOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexID(indexId)
- {
- }
-
- const RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
-};
-
-class DeleteIndexOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- {
- return adoptRef(new DeleteIndexOperation(transaction, objectStoreId, indexMetadata));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- int64_t objectStoreID() const { return m_objectStoreID; }
- const IDBIndexMetadata& idbIndexMetadata() const { return m_indexMetadata; }
-
-private:
- DeleteIndexOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexMetadata(indexMetadata)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const IDBIndexMetadata m_indexMetadata;
-};
-
-class DeleteIndexAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- {
- return adoptRef(new DeleteIndexAbortOperation(transaction, objectStoreId, indexMetadata));
- }
- virtual void perform() override final;
-private:
- DeleteIndexAbortOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexMetadata(indexMetadata)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const IDBIndexMetadata m_indexMetadata;
-};
-
-class GetOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new GetOperation(transaction, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- int64_t objectStoreID() const { return m_objectStoreID; }
- int64_t indexID() const { return m_indexID; }
- IndexedDB::CursorType cursorType() const { return m_cursorType; }
- IDBKeyRange* keyRange() const { return m_keyRange.get(); }
- bool autoIncrement() const { return m_autoIncrement; }
- IDBKeyPath keyPath() const { return m_keyPath; }
-
-private:
- GetOperation(IDBTransactionBackend* transaction, const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexID(indexId)
- , m_keyPath(metadata.objectStores.get(objectStoreId).keyPath)
- , m_autoIncrement(metadata.objectStores.get(objectStoreId).autoIncrement)
- , m_keyRange(keyRange)
- , m_cursorType(cursorType)
- , m_callbacks(callbacks)
- {
- ASSERT(metadata.objectStores.contains(objectStoreId));
- ASSERT(metadata.objectStores.get(objectStoreId).id == objectStoreId);
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
- const IDBKeyPath m_keyPath;
- const bool m_autoIncrement;
- const RefPtr<IDBKeyRange> m_keyRange;
- const IndexedDB::CursorType m_cursorType;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class PutOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
- {
- return adoptRef(new PutOperation(transaction, objectStore, value, key, putMode, callbacks, indexIds, indexKeys));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- IDBDatabaseBackend::PutMode putMode() const { return m_putMode; }
- const IDBObjectStoreMetadata& objectStore() const { return m_objectStore; }
- IDBKey* key() const { return m_key.get(); }
- const Vector<int64_t>& indexIDs() const { return m_indexIDs; }
- const Vector<IndexKeys>& indexKeys() const { return m_indexKeys; }
- SharedBuffer* value() const { return m_value.get(); }
-
-private:
- PutOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore, PassRefPtr<SharedBuffer>& value, PassRefPtr<IDBKey> key, IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
- : m_transaction(transaction)
- , m_objectStore(objectStore)
- , m_value(value)
- , m_key(key)
- , m_putMode(putMode)
- , m_callbacks(callbacks)
- , m_indexIDs(indexIds)
- , m_indexKeys(indexKeys)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const IDBObjectStoreMetadata m_objectStore;
- const RefPtr<SharedBuffer> m_value;
- const RefPtr<IDBKey> m_key;
- const IDBDatabaseBackend::PutMode m_putMode;
- const RefPtr<IDBCallbacks> m_callbacks;
- const Vector<int64_t> m_indexIDs;
- const Vector<IndexKeys> m_indexKeys;
-};
-
-class SetIndexesReadyOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, size_t indexCount)
- {
- return adoptRef(new SetIndexesReadyOperation(transaction, indexCount));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-private:
- SetIndexesReadyOperation(IDBTransactionBackend* transaction, size_t indexCount)
- : m_transaction(transaction)
- , m_indexCount(indexCount)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const size_t m_indexCount;
-};
-
-class OpenCursorOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new OpenCursorOperation(transaction, objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- int64_t transactionID() const { return m_transaction->id(); }
- int64_t objectStoreID() const { return m_objectStoreID; }
- int64_t indexID() const { return m_indexID; }
- IndexedDB::CursorDirection direction() const { return m_direction; }
- IndexedDB::CursorType cursorType() const { return m_cursorType; }
- IDBDatabaseBackend::TaskType taskType() const { return m_taskType; }
- IDBKeyRange* keyRange() const { return m_keyRange.get(); }
- IndexedDB::CursorDirection cursorDirection() const { return m_direction; }
-
-private:
- OpenCursorOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexID(indexId)
- , m_keyRange(keyRange)
- , m_direction(direction)
- , m_cursorType(cursorType)
- , m_taskType(taskType)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
- const PassRefPtr<IDBKeyRange> m_keyRange;
- const IndexedDB::CursorDirection m_direction;
- const IndexedDB::CursorType m_cursorType;
- const IDBDatabaseBackend::TaskType m_taskType;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class CountOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new CountOperation(transaction, objectStoreId, indexId, keyRange, callbacks));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- int64_t objectStoreID() const { return m_objectStoreID; }
- int64_t indexID() const { return m_indexID; }
- IDBKeyRange* keyRange() const { return m_keyRange.get(); }
-
-private:
- CountOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexID(indexId)
- , m_keyRange(keyRange)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
- const RefPtr<IDBKeyRange> m_keyRange;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class DeleteRangeOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new DeleteRangeOperation(transaction, objectStoreId, keyRange, callbacks));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- int64_t objectStoreID() const { return m_objectStoreID; }
- IDBKeyRange* keyRange() const { return m_keyRange.get(); }
-
-private:
- DeleteRangeOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_keyRange(keyRange)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const RefPtr<IDBKeyRange> m_keyRange;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class ClearObjectStoreOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new ClearObjectStoreOperation(transaction, objectStoreId, callbacks));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- IDBTransactionBackend* transaction() const { return m_transaction.get(); }
- int64_t objectStoreID() const { return m_objectStoreID; }
-
-private:
- ClearObjectStoreOperation(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBTransactionBackendOperations_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp b/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp
deleted file mode 100644
index dd4f86fbf..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp
+++ /dev/null
@@ -1,152 +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.
- *
- * 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 "IDBTransactionCoordinator.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBDatabaseCallbacks.h"
-#include "IDBTransactionBackend.h"
-
-namespace WebCore {
-
-PassOwnPtr<IDBTransactionCoordinator> IDBTransactionCoordinator::create()
-{
- return adoptPtr(new IDBTransactionCoordinator());
-}
-
-IDBTransactionCoordinator::IDBTransactionCoordinator()
-{
-}
-
-IDBTransactionCoordinator::~IDBTransactionCoordinator()
-{
-}
-
-void IDBTransactionCoordinator::didCreateTransaction(IDBTransactionBackend* transaction)
-{
- ASSERT(!m_transactions.contains(transaction));
- m_transactions.add(transaction, transaction);
-}
-
-void IDBTransactionCoordinator::didStartTransaction(IDBTransactionBackend* transaction)
-{
- ASSERT(m_transactions.contains(transaction));
-
- m_queuedTransactions.add(transaction);
- processStartedTransactions();
-}
-
-void IDBTransactionCoordinator::didFinishTransaction(IDBTransactionBackend* transaction)
-{
- ASSERT(m_transactions.contains(transaction));
-
- if (m_queuedTransactions.contains(transaction)) {
- ASSERT(!m_startedTransactions.contains(transaction));
- m_queuedTransactions.remove(transaction);
- } else if (m_startedTransactions.contains(transaction))
- m_startedTransactions.remove(transaction);
-
- m_transactions.remove(transaction);
-
- processStartedTransactions();
-}
-
-#ifndef NDEBUG
-// Verifies internal consistiency while returning whether anything is found.
-bool IDBTransactionCoordinator::isActive(IDBTransactionBackend* transaction)
-{
- bool found = false;
- if (m_queuedTransactions.contains(transaction))
- found = true;
- if (m_startedTransactions.contains(transaction)) {
- ASSERT(!found);
- found = true;
- }
- ASSERT(found == m_transactions.contains(transaction));
- return found;
-}
-#endif
-
-void IDBTransactionCoordinator::processStartedTransactions()
-{
- if (m_queuedTransactions.isEmpty())
- return;
-
- ASSERT(m_startedTransactions.isEmpty() || (*m_startedTransactions.begin())->mode() != IndexedDB::TransactionMode::VersionChange);
-
- ListHashSet<IDBTransactionBackend*>::const_iterator it = m_queuedTransactions.begin();
- while (it != m_queuedTransactions.end()) {
- IDBTransactionBackend* transaction = *it;
- ++it;
- if (canRunTransaction(transaction)) {
- m_queuedTransactions.remove(transaction);
- m_startedTransactions.add(transaction);
- transaction->run();
- }
- }
-}
-
-static bool doScopesOverlap(const HashSet<int64_t>& scope1, const HashSet<int64_t>& scope2)
-{
- for (HashSet<int64_t>::const_iterator it = scope1.begin(); it != scope1.end(); ++it) {
- if (scope2.contains(*it))
- return true;
- }
- return false;
-}
-
-bool IDBTransactionCoordinator::canRunTransaction(IDBTransactionBackend* transaction)
-{
- ASSERT(m_queuedTransactions.contains(transaction));
- switch (transaction->mode()) {
- case IndexedDB::TransactionMode::VersionChange:
- ASSERT(m_queuedTransactions.size() == 1);
- ASSERT(m_startedTransactions.isEmpty());
- return true;
-
- case IndexedDB::TransactionMode::ReadOnly:
- return true;
-
- case IndexedDB::TransactionMode::ReadWrite:
- for (HashSet<IDBTransactionBackend*>::const_iterator it = m_startedTransactions.begin(); it != m_startedTransactions.end(); ++it) {
- if ((*it)->mode() == IndexedDB::TransactionMode::ReadWrite && doScopesOverlap(transaction->scope(), (*it)->scope()))
- return false;
- }
- for (ListHashSet<IDBTransactionBackend*>::const_iterator it = m_queuedTransactions.begin(); *it != transaction; ++it) {
- ASSERT(it != m_queuedTransactions.end());
- if ((*it)->mode() == IndexedDB::TransactionMode::ReadWrite && doScopesOverlap(transaction->scope(), (*it)->scope()))
- return false;
- }
- return true;
- }
- ASSERT_NOT_REACHED();
- return false;
-}
-
-};
-
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h b/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h
deleted file mode 100644
index 19878091b..000000000
--- a/Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h
+++ /dev/null
@@ -1,72 +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.
- *
- * 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 IDBTransactionCoordinator_h
-#define IDBTransactionCoordinator_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBTransactionBackend;
-
-// Transactions are executed in the order the were created.
-class IDBTransactionCoordinator {
-public:
- static PassOwnPtr<IDBTransactionCoordinator> create();
- virtual ~IDBTransactionCoordinator();
-
- // Called by transactions as they start and finish.
- void didCreateTransaction(IDBTransactionBackend*);
- void didOpenBackingStoreTransaction(IDBTransactionBackend*);
- void didStartTransaction(IDBTransactionBackend*);
- void didFinishTransaction(IDBTransactionBackend*);
-
-#ifndef NDEBUG
- bool isActive(IDBTransactionBackend*);
-#endif
-
-private:
- IDBTransactionCoordinator();
-
- void processStartedTransactions();
- bool canRunTransaction(IDBTransactionBackend*);
-
- // This is just an efficient way to keep references to all transactions.
- HashMap<IDBTransactionBackend*, RefPtr<IDBTransactionBackend> > m_transactions;
- // Transactions in different states are grouped below.
- ListHashSet<IDBTransactionBackend*> m_queuedTransactions;
- HashSet<IDBTransactionBackend*> m_startedTransactions;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBTransactionCoordinator_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBTransactionMode.h b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.h
new file mode 100644
index 000000000..a089ccf3a
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+enum class IDBTransactionMode {
+ Readonly,
+ Readwrite,
+ Versionchange
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/Modules/indexeddb/IDBAny.idl b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl
index fb480c92e..7a7eb9306 100644
--- a/Source/WebCore/Modules/indexeddb/IDBAny.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -24,10 +24,9 @@
*/
[
- NoInterfaceObject,
Conditional=INDEXED_DATABASE,
- CustomToJSObject,
- JSNoStaticTables
-] interface IDBAny {
- // This space is intentionally left blank.
+] enum IDBTransactionMode {
+ "readonly",
+ "readwrite",
+ "versionchange"
};
diff --git a/Source/WebCore/Modules/indexeddb/IDBValue.cpp b/Source/WebCore/Modules/indexeddb/IDBValue.cpp
new file mode 100644
index 000000000..d824707c9
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBValue.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBValue.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "SerializedScriptValue.h"
+#include <wtf/CrossThreadTask.h>
+
+namespace WebCore {
+
+IDBValue::IDBValue()
+{
+}
+
+IDBValue::IDBValue(const SerializedScriptValue& scriptValue)
+ : m_data(ThreadSafeDataBuffer::copyVector(scriptValue.data()))
+ , m_blobURLs(scriptValue.blobURLsIsolatedCopy())
+{
+}
+
+IDBValue::IDBValue(const ThreadSafeDataBuffer& value)
+ : m_data(value)
+{
+}
+
+IDBValue::IDBValue(const SerializedScriptValue& scriptValue, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths)
+ : m_data(ThreadSafeDataBuffer::copyVector(scriptValue.data()))
+ , m_blobURLs(blobURLs)
+ , m_blobFilePaths(blobFilePaths)
+{
+ ASSERT(m_data.data());
+}
+
+IDBValue::IDBValue(const ThreadSafeDataBuffer& value, Vector<String>&& blobURLs, Vector<String>&& blobFilePaths)
+ : m_data(value)
+ , m_blobURLs(WTFMove(blobURLs))
+ , m_blobFilePaths(WTFMove(blobFilePaths))
+{
+}
+
+IDBValue::IDBValue(const ThreadSafeDataBuffer& value, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths)
+ : m_data(value)
+ , m_blobURLs(blobURLs)
+ , m_blobFilePaths(blobFilePaths)
+{
+}
+
+void IDBValue::setAsIsolatedCopy(const IDBValue& other)
+{
+ ASSERT(m_blobURLs.isEmpty() && m_blobFilePaths.isEmpty());
+
+ m_data = other.m_data;
+ m_blobURLs = CrossThreadCopier<Vector<String>>::copy(other.m_blobURLs);
+ m_blobFilePaths = CrossThreadCopier<Vector<String>>::copy(other.m_blobFilePaths);
+}
+
+IDBValue IDBValue::isolatedCopy() const
+{
+ IDBValue result;
+ result.setAsIsolatedCopy(*this);
+ return result;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBValue.h b/Source/WebCore/Modules/indexeddb/IDBValue.h
new file mode 100644
index 000000000..2b2cfaca1
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/IDBValue.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+#if ENABLE(INDEXED_DATABASE)
+
+#include "ThreadSafeDataBuffer.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class SerializedScriptValue;
+
+class IDBValue {
+public:
+ WEBCORE_EXPORT IDBValue();
+ IDBValue(const SerializedScriptValue&);
+ IDBValue(const ThreadSafeDataBuffer&);
+ IDBValue(const SerializedScriptValue&, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths);
+ IDBValue(const ThreadSafeDataBuffer&, Vector<String>&& blobURLs, Vector<String>&& blobFilePaths);
+ IDBValue(const ThreadSafeDataBuffer&, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths);
+
+ void setAsIsolatedCopy(const IDBValue&);
+ IDBValue isolatedCopy() const;
+
+ const ThreadSafeDataBuffer& data() const { return m_data; }
+ const Vector<String>& blobURLs() const { return m_blobURLs; }
+ const Vector<String>& blobFilePaths() const { return m_blobFilePaths; }
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBValue&);
+
+private:
+ ThreadSafeDataBuffer m_data;
+ Vector<String> m_blobURLs;
+ Vector<String> m_blobFilePaths;
+};
+
+
+template<class Encoder>
+void IDBValue::encode(Encoder& encoder) const
+{
+ encoder << m_data;
+ encoder << m_blobURLs;
+ encoder << m_blobFilePaths;
+}
+
+template<class Decoder>
+bool IDBValue::decode(Decoder& decoder, IDBValue& result)
+{
+ if (!decoder.decode(result.m_data))
+ return false;
+
+ if (!decoder.decode(result.m_blobURLs))
+ return false;
+
+ if (!decoder.decode(result.m_blobFilePaths))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp
index 94f3c01aa..e84a733c2 100644
--- a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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"
@@ -30,15 +30,21 @@
namespace WebCore {
-IDBVersionChangeEvent::IDBVersionChangeEvent(unsigned long long oldVersion, unsigned long long newVersion, IndexedDB::VersionNullness newVersionNullness, const AtomicString& eventType)
- : Event(eventType, false /*canBubble*/, false /*cancelable*/)
+IDBVersionChangeEvent::IDBVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion, const AtomicString& name)
+ : Event(name, false /*canBubble*/, false /*cancelable*/)
+ , m_requestIdentifier(requestIdentifier)
, m_oldVersion(oldVersion)
- , m_newVersion(newVersion)
- , m_newVersionNullness(newVersionNullness)
{
+ if (newVersion)
+ m_newVersion = newVersion;
+ else
+ m_newVersion = std::nullopt;
}
-IDBVersionChangeEvent::~IDBVersionChangeEvent()
+IDBVersionChangeEvent::IDBVersionChangeEvent(const AtomicString& name, const Init& init, IsTrusted isTrusted)
+ : Event(name, init, isTrusted)
+ , m_oldVersion(init.oldVersion)
+ , m_newVersion(init.newVersion)
{
}
@@ -49,4 +55,4 @@ EventInterface IDBVersionChangeEvent::eventInterface() const
} // namespace WebCore
-#endif
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h
index c8d200885..0801720bc 100644
--- a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h
+++ b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h
@@ -1,65 +1,82 @@
/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2015, 2016 Apple 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.
*
- * 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.
- *
- * 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.
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBVersionChangeEvent_h
-#define IDBVersionChangeEvent_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
#include "Event.h"
-#include "IndexedDB.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
+#include "IDBResourceIdentifier.h"
+#include <wtf/Optional.h>
namespace WebCore {
-class IDBVersionChangeEvent : public Event {
+class IDBVersionChangeEvent final : public Event {
public:
- static PassRefPtr<IDBVersionChangeEvent> create(unsigned long long oldVersion = 0, unsigned long long newVersion = 0, IndexedDB::VersionNullness newVersionNullness = IndexedDB::VersionNullness::Null, const AtomicString& eventType = AtomicString())
+ static Ref<IDBVersionChangeEvent> create(uint64_t oldVersion, uint64_t newVersion, const AtomicString& eventType)
+ {
+ return adoptRef(*new IDBVersionChangeEvent(IDBResourceIdentifier::emptyValue(), oldVersion, newVersion, eventType));
+ }
+
+ static Ref<IDBVersionChangeEvent> create(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion, const AtomicString& eventType)
{
- return adoptRef(new IDBVersionChangeEvent(oldVersion, newVersion, newVersionNullness, eventType));
+ return adoptRef(*new IDBVersionChangeEvent(requestIdentifier, oldVersion, newVersion, eventType));
}
- virtual ~IDBVersionChangeEvent();
+ struct Init : EventInit {
+ uint64_t oldVersion { 0 };
+ std::optional<uint64_t> newVersion;
+ };
- virtual unsigned long long oldVersion() { return m_oldVersion; }
- virtual unsigned long long newVersion(bool& isNull) { isNull = (m_newVersionNullness == IndexedDB::VersionNullness::Null); return m_newVersion; }
+ static Ref<IDBVersionChangeEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No)
+ {
+ return adoptRef(*new IDBVersionChangeEvent(type, initializer, isTrusted));
+ }
- virtual EventInterface eventInterface() const;
+ const IDBResourceIdentifier& requestIdentifier() const { return m_requestIdentifier; }
+
+ bool isVersionChangeEvent() const final { return true; }
+
+ uint64_t oldVersion() const { return m_oldVersion; }
+ std::optional<uint64_t> newVersion() const { return m_newVersion; }
private:
- IDBVersionChangeEvent(unsigned long long oldVersion, unsigned long long newVersion, IndexedDB::VersionNullness newVersionNullness, const AtomicString& eventType);
+ IDBVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion, const AtomicString& eventType);
+ IDBVersionChangeEvent(const AtomicString&, const Init&, IsTrusted);
+
+ EventInterface eventInterface() const;
- unsigned long long m_oldVersion;
- unsigned long long m_newVersion;
- IndexedDB::VersionNullness m_newVersionNullness;
+ IDBResourceIdentifier m_requestIdentifier;
+ uint64_t m_oldVersion;
+ std::optional<uint64_t> m_newVersion;
};
} // namespace WebCore
-#endif // ENABLE(INDEXED_DATABASE)
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::IDBVersionChangeEvent)
+ static bool isType(const WebCore::Event& event) { return event.isVersionChangeEvent(); }
+SPECIALIZE_TYPE_TRAITS_END()
-#endif // IDBVersionChangeEvent_h
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl
index 49163e267..e696e5302 100644
--- a/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl
@@ -23,10 +23,16 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+// FIXME: This should be exposed to workers as well.
[
Conditional=INDEXED_DATABASE,
- JSNoStaticTables,
+ Constructor(DOMString type, optional IDBVersionChangeEventInit eventInitDict),
] interface IDBVersionChangeEvent : Event {
readonly attribute unsigned long long oldVersion;
readonly attribute unsigned long long? newVersion;
};
+
+dictionary IDBVersionChangeEventInit : EventInit {
+ unsigned long long oldVersion = 0;
+ unsigned long long? newVersion = null;
+};
diff --git a/Source/WebCore/Modules/indexeddb/IndexedDB.h b/Source/WebCore/Modules/indexeddb/IndexedDB.h
index fac392621..0ed653e8f 100644
--- a/Source/WebCore/Modules/indexeddb/IndexedDB.h
+++ b/Source/WebCore/Modules/indexeddb/IndexedDB.h
@@ -23,8 +23,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IndexedDB_h
-#define IndexedDB_h
+#pragma once
#if ENABLE(INDEXED_DATABASE)
@@ -32,18 +31,19 @@ namespace WebCore {
namespace IndexedDB {
-enum class TransactionMode {
- ReadOnly = 0,
- ReadWrite = 1,
- VersionChange = 2,
+enum class TransactionState {
+ Active,
+ Inactive,
+ Committing,
+ Aborting,
+ Finished,
};
-const unsigned TransactionModeMaximum = 2;
enum class CursorDirection {
- Next = 0,
- NextNoDuplicate = 1,
- Prev = 2,
- PrevNoDuplicate = 3,
+ Next,
+ Nextunique,
+ Prev,
+ Prevunique,
};
const unsigned CursorDirectionMaximum = 3;
@@ -53,15 +53,57 @@ enum class CursorType {
};
const unsigned CursorTypeMaximum = 1;
+enum class CursorSource {
+ Index,
+ ObjectStore,
+};
+
enum class VersionNullness {
Null,
NonNull,
};
+enum class ObjectStoreOverwriteMode {
+ Overwrite,
+ OverwriteForCursor,
+ NoOverwrite,
+};
+
+enum class IndexRecordType {
+ Key,
+ Value,
+};
+
+enum class ObjectStoreRecordType {
+ ValueOnly,
+ KeyOnly,
+};
+
+// In order of the least to the highest precedent in terms of sort order.
+enum KeyType {
+ Max = -1,
+ Invalid = 0,
+ Array,
+ Binary,
+ String,
+ Date,
+ Number,
+ Min,
+};
+
+enum class RequestType {
+ Open,
+ Delete,
+ Other,
+};
+
+enum class GetAllType {
+ Keys,
+ Values,
+};
+
} // namespace IndexedDB
} // namespace WebCore
#endif // ENABLED(INDEXED_DATABASE)
-
-#endif // IndexedDB_h
diff --git a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp b/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp
deleted file mode 100644
index a7cbcd797..000000000
--- a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2012, 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.
- *
- * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. 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 INC. 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 "PageGroupIndexedDatabase.h"
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "GroupSettings.h"
-#include "IDBFactoryBackendInterface.h"
-#include "PageGroup.h"
-
-namespace WebCore {
-
-PageGroupIndexedDatabase::PageGroupIndexedDatabase(const String& databaseDirectoryIdentifier)
- : m_databaseDirectoryIdentifier(databaseDirectoryIdentifier)
-{
-}
-
-PageGroupIndexedDatabase::~PageGroupIndexedDatabase()
-{
-}
-
-const char* PageGroupIndexedDatabase::supplementName()
-{
- return "PageGroupIndexedDatabase";
-}
-
-PageGroupIndexedDatabase* PageGroupIndexedDatabase::from(PageGroup& group)
-{
- PageGroupIndexedDatabase* supplement = static_cast<PageGroupIndexedDatabase*>(Supplement<PageGroup>::from(&group, supplementName()));
- if (!supplement) {
- supplement = new PageGroupIndexedDatabase(group.groupSettings().indexedDBDatabasePath());
- provideTo(&group, supplementName(), adoptPtr(supplement));
- }
- return supplement;
-}
-
-IDBFactoryBackendInterface* PageGroupIndexedDatabase::factoryBackend()
-{
- // Do not add page setting based access control here since this object is shared by all pages in
- // the group and having per-page controls is misleading.
- if (!m_factoryBackend)
- m_factoryBackend = IDBFactoryBackendInterface::create(m_databaseDirectoryIdentifier);
- return m_factoryBackend.get();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h b/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h
deleted file mode 100644
index 92a25582d..000000000
--- a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012, 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.
- *
- * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. 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 INC. 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 PageGroupIndexedDatabase_h
-#define PageGroupIndexedDatabase_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "Supplementable.h"
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class IDBFactoryBackendInterface;
-class PageGroup;
-
-class PageGroupIndexedDatabase : public Supplement<PageGroup> {
-public:
- virtual ~PageGroupIndexedDatabase();
- static PageGroupIndexedDatabase* from(PageGroup&);
-
- IDBFactoryBackendInterface* factoryBackend();
-
-private:
- explicit PageGroupIndexedDatabase(const String& databaseDirectoryIdentifier);
- static const char* supplementName();
-
- String m_databaseDirectoryIdentifier;
- RefPtr<IDBFactoryBackendInterface> m_factoryBackend;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // PageGroupIndexedDatabase_h
diff --git a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp
index 2074b15c5..4ab415653 100644
--- a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp
+++ b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp
@@ -11,10 +11,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* 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
@@ -27,19 +27,20 @@
#include "config.h"
-#if ENABLE(INDEXED_DATABASE)
+#if ENABLE(INDEXED_DATABASE_IN_WORKERS)
#include "WorkerGlobalScopeIndexedDatabase.h"
+#include "IDBConnectionProxy.h"
#include "IDBFactory.h"
-#include "IDBFactoryBackendInterface.h"
+#include "IDBOpenDBRequest.h"
#include "ScriptExecutionContext.h"
-#include "SecurityOrigin.h"
+#include "WorkerGlobalScope.h"
namespace WebCore {
-WorkerGlobalScopeIndexedDatabase::WorkerGlobalScopeIndexedDatabase(const String& databaseDirectoryIdentifier)
- : m_databaseDirectoryIdentifier(databaseDirectoryIdentifier)
+WorkerGlobalScopeIndexedDatabase::WorkerGlobalScopeIndexedDatabase(WorkerGlobalScope&, IDBClient::IDBConnectionProxy& connectionProxy)
+ : m_connectionProxy(connectionProxy)
{
}
@@ -52,36 +53,35 @@ const char* WorkerGlobalScopeIndexedDatabase::supplementName()
return "WorkerGlobalScopeIndexedDatabase";
}
-WorkerGlobalScopeIndexedDatabase* WorkerGlobalScopeIndexedDatabase::from(ScriptExecutionContext* context)
+WorkerGlobalScopeIndexedDatabase* WorkerGlobalScopeIndexedDatabase::from(WorkerGlobalScope& scope)
{
- WorkerGlobalScopeIndexedDatabase* supplement = static_cast<WorkerGlobalScopeIndexedDatabase*>(Supplement<ScriptExecutionContext>::from(context, supplementName()));
+ WorkerGlobalScopeIndexedDatabase* supplement = static_cast<WorkerGlobalScopeIndexedDatabase*>(Supplement<WorkerGlobalScope>::from(&scope, supplementName()));
if (!supplement) {
- String databaseDirectoryIdentifier;
- WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
- const GroupSettings* groupSettings = workerGlobalScope->groupSettings();
- if (groupSettings)
- databaseDirectoryIdentifier = groupSettings->indexedDBDatabasePath();
+ auto* connectionProxy = scope.idbConnectionProxy();
+ if (!connectionProxy)
+ return nullptr;
- supplement = new WorkerGlobalScopeIndexedDatabase(databaseDirectoryIdentifier);
- provideTo(context, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<WorkerGlobalScopeIndexedDatabase>(scope, *connectionProxy);
+ supplement = newSupplement.get();
+ provideTo(&scope, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
-IDBFactory* WorkerGlobalScopeIndexedDatabase::indexedDB(ScriptExecutionContext* context)
+IDBFactory* WorkerGlobalScopeIndexedDatabase::indexedDB(WorkerGlobalScope& scope)
{
- return from(context)->indexedDB();
+ auto* scopeIDB = from(scope);
+ return scopeIDB ? scopeIDB->indexedDB() : nullptr;
}
IDBFactory* WorkerGlobalScopeIndexedDatabase::indexedDB()
{
- if (!m_factoryBackend)
- m_factoryBackend = IDBFactoryBackendInterface::create(m_databaseDirectoryIdentifier);
if (!m_idbFactory)
- m_idbFactory = IDBFactory::create(m_factoryBackend.get());
+ m_idbFactory = IDBFactory::create(m_connectionProxy.get());
+
return m_idbFactory.get();
}
} // namespace WebCore
-#endif // ENABLE(INDEXED_DATABASE)
+#endif // ENABLE(INDEXED_DATABASE_IN_WORKERS)
diff --git a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h
index c00b0ab56..20fe4654f 100644
--- a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h
+++ b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2013, 2014, 2015, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* 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
@@ -24,10 +24,9 @@
*
*/
-#ifndef WorkerGlobalScopeIndexedDatabase_h
-#define WorkerGlobalScopeIndexedDatabase_h
+#pragma once
-#if ENABLE(INDEXED_DATABASE)
+#if ENABLE(INDEXED_DATABASE_IN_WORKERS)
#include "Supplementable.h"
#include <wtf/text/WTFString.h>
@@ -35,29 +34,29 @@
namespace WebCore {
class IDBFactory;
-class IDBFactoryBackendInterface;
-class ScriptExecutionContext;
+class WorkerGlobalScope;
-class WorkerGlobalScopeIndexedDatabase : public Supplement<ScriptExecutionContext> {
+namespace IDBClient {
+class IDBConnectionProxy;
+}
+
+class WorkerGlobalScopeIndexedDatabase : public Supplement<WorkerGlobalScope> {
public:
+ explicit WorkerGlobalScopeIndexedDatabase(WorkerGlobalScope&, IDBClient::IDBConnectionProxy&);
virtual ~WorkerGlobalScopeIndexedDatabase();
- static WorkerGlobalScopeIndexedDatabase* from(ScriptExecutionContext*);
- static IDBFactory* indexedDB(ScriptExecutionContext*);
+ static IDBFactory* indexedDB(WorkerGlobalScope&);
private:
- explicit WorkerGlobalScopeIndexedDatabase(const String& databaseDirectoryIdentifier);
-
IDBFactory* indexedDB();
+
static const char* supplementName();
+ static WorkerGlobalScopeIndexedDatabase* from(WorkerGlobalScope&);
- String m_databaseDirectoryIdentifier;
- RefPtr<IDBFactoryBackendInterface> m_factoryBackend;
RefPtr<IDBFactory> m_idbFactory;
+ Ref<IDBClient::IDBConnectionProxy> m_connectionProxy;
};
} // namespace WebCore
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // WorkerGlobalScopeIndexedDatabase_h
+#endif // ENABLE(INDEXED_DATABASE_IN_WORKERS)
diff --git a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl
index 9bee7303a..fc7f775d5 100644
--- a/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl
+++ b/Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2016 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* 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
@@ -25,31 +25,8 @@
*/
[
- Conditional=INDEXED_DATABASE,
+ Conditional=INDEXED_DATABASE_IN_WORKERS,
] partial interface WorkerGlobalScope {
- [ImplementedAs=indexedDB] readonly attribute IDBFactory webkitIndexedDB;
-
- attribute IDBCursorConstructor webkitIDBCursor;
- attribute IDBDatabaseConstructor webkitIDBDatabase;
- attribute IDBFactoryConstructor webkitIDBFactory;
- attribute IDBIndexConstructor webkitIDBIndex;
- attribute IDBKeyRangeConstructor webkitIDBKeyRange;
- attribute IDBObjectStoreConstructor webkitIDBObjectStore;
- attribute IDBRequestConstructor webkitIDBRequest;
- attribute IDBTransactionConstructor webkitIDBTransaction;
-
- readonly attribute IDBFactory indexedDB;
-
- attribute IDBCursorConstructor IDBCursor;
- attribute IDBCursorWithValueConstructor IDBCursorWithValue;
- attribute IDBDatabaseConstructor IDBDatabase;
- attribute IDBFactoryConstructor IDBFactory;
- attribute IDBIndexConstructor IDBIndex;
- attribute IDBKeyRangeConstructor IDBKeyRange;
- attribute IDBObjectStoreConstructor IDBObjectStore;
- attribute IDBOpenDBRequestConstructor IDBOpenDBRequest;
- attribute IDBRequestConstructor IDBRequest;
- attribute IDBTransactionConstructor IDBTransaction;
- attribute IDBVersionChangeEventConstructor IDBVersionChangeEvent;
+ [EnabledAtRuntime=IndexedDBWorkers] readonly attribute IDBFactory indexedDB;
};
diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp
new file mode 100644
index 000000000..4db50c0b9
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBConnectionProxy.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IDBDatabase.h"
+#include "IDBGetRecordData.h"
+#include "IDBIterateCursorData.h"
+#include "IDBKeyRangeData.h"
+#include "IDBOpenDBRequest.h"
+#include "IDBRequestData.h"
+#include "IDBResultData.h"
+#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+namespace IDBClient {
+
+IDBConnectionProxy::IDBConnectionProxy(IDBConnectionToServer& connection)
+ : m_connectionToServer(connection)
+ , m_serverConnectionIdentifier(connection.identifier())
+{
+ ASSERT(isMainThread());
+}
+
+void IDBConnectionProxy::ref()
+{
+ m_connectionToServer.ref();
+}
+
+void IDBConnectionProxy::deref()
+{
+ m_connectionToServer.deref();
+}
+
+Ref<IDBOpenDBRequest> IDBConnectionProxy::openDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version)
+{
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> locker(m_openDBRequestMapLock);
+
+ request = IDBOpenDBRequest::createOpenRequest(context, *this, databaseIdentifier, version);
+ ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier()));
+ m_openDBRequestMap.set(request->resourceIdentifier(), request.get());
+ }
+
+ callConnectionOnMainThread(&IDBConnectionToServer::openDatabase, IDBRequestData(*this, *request));
+
+ return request.releaseNonNull();
+}
+
+Ref<IDBOpenDBRequest> IDBConnectionProxy::deleteDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier)
+{
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> locker(m_openDBRequestMapLock);
+
+ request = IDBOpenDBRequest::createDeleteRequest(context, *this, databaseIdentifier);
+ ASSERT(!m_openDBRequestMap.contains(request->resourceIdentifier()));
+ m_openDBRequestMap.set(request->resourceIdentifier(), request.get());
+ }
+
+ callConnectionOnMainThread(&IDBConnectionToServer::deleteDatabase, IDBRequestData(*this, *request));
+
+ return request.releaseNonNull();
+}
+
+void IDBConnectionProxy::didOpenDatabase(const IDBResultData& resultData)
+{
+ completeOpenDBRequest(resultData);
+}
+
+void IDBConnectionProxy::didDeleteDatabase(const IDBResultData& resultData)
+{
+ completeOpenDBRequest(resultData);
+}
+
+void IDBConnectionProxy::completeOpenDBRequest(const IDBResultData& resultData)
+{
+ ASSERT(isMainThread());
+
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> locker(m_openDBRequestMapLock);
+ request = m_openDBRequestMap.take(resultData.requestIdentifier());
+ }
+
+ if (!request)
+ return;
+
+ request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, resultData);
+}
+
+void IDBConnectionProxy::createObjectStore(TransactionOperation& operation, const IDBObjectStoreInfo& info)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::createObjectStore, requestData, info);
+}
+
+void IDBConnectionProxy::renameObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& newName)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::renameObjectStore, requestData, objectStoreIdentifier, newName);
+}
+
+void IDBConnectionProxy::renameIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::renameIndex, requestData, objectStoreIdentifier, indexIdentifier, newName);
+}
+
+void IDBConnectionProxy::deleteObjectStore(TransactionOperation& operation, const String& objectStoreName)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::deleteObjectStore, requestData, objectStoreName);
+}
+
+void IDBConnectionProxy::clearObjectStore(TransactionOperation& operation, uint64_t objectStoreIdentifier)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::clearObjectStore, requestData, objectStoreIdentifier);
+}
+
+void IDBConnectionProxy::createIndex(TransactionOperation& operation, const IDBIndexInfo& info)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::createIndex, requestData, info);
+}
+
+void IDBConnectionProxy::deleteIndex(TransactionOperation& operation, uint64_t objectStoreIdentifier, const String& indexName)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::deleteIndex, requestData, WTFMove(objectStoreIdentifier), indexName);
+}
+
+void IDBConnectionProxy::putOrAdd(TransactionOperation& operation, IDBKeyData&& keyData, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode mode)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::putOrAdd, requestData, keyData, value, mode);
+}
+
+void IDBConnectionProxy::getRecord(TransactionOperation& operation, const IDBGetRecordData& getRecordData)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::getRecord, requestData, getRecordData);
+}
+
+void IDBConnectionProxy::getAllRecords(TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::getAllRecords, requestData, getAllRecordsData);
+}
+
+void IDBConnectionProxy::getCount(TransactionOperation& operation, const IDBKeyRangeData& keyRange)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::getCount, requestData, keyRange);
+}
+
+void IDBConnectionProxy::deleteRecord(TransactionOperation& operation, const IDBKeyRangeData& keyRange)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::deleteRecord, requestData, keyRange);
+}
+
+void IDBConnectionProxy::openCursor(TransactionOperation& operation, const IDBCursorInfo& info)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::openCursor, requestData, info);
+}
+
+void IDBConnectionProxy::iterateCursor(TransactionOperation& operation, const IDBIterateCursorData& data)
+{
+ const IDBRequestData requestData { operation };
+ saveOperation(operation);
+
+ callConnectionOnMainThread(&IDBConnectionToServer::iterateCursor, requestData, data);
+}
+
+void IDBConnectionProxy::saveOperation(TransactionOperation& operation)
+{
+ Locker<Lock> locker(m_transactionOperationLock);
+
+ ASSERT(!m_activeOperations.contains(operation.identifier()));
+ m_activeOperations.set(operation.identifier(), &operation);
+}
+
+void IDBConnectionProxy::completeOperation(const IDBResultData& resultData)
+{
+ RefPtr<TransactionOperation> operation;
+ {
+ Locker<Lock> locker(m_transactionOperationLock);
+ operation = m_activeOperations.take(resultData.requestIdentifier());
+ }
+
+ if (!operation)
+ return;
+
+ operation->transitionToComplete(resultData, WTFMove(operation));
+}
+
+void IDBConnectionProxy::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ callConnectionOnMainThread(&IDBConnectionToServer::abortOpenAndUpgradeNeeded, databaseConnectionIdentifier, transactionIdentifier);
+}
+
+void IDBConnectionProxy::fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion)
+{
+ RefPtr<IDBDatabase> database;
+ {
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+ database = m_databaseConnectionMap.get(databaseConnectionIdentifier);
+ }
+
+ if (!database)
+ return;
+
+ database->performCallbackOnOriginThread(*database, &IDBDatabase::fireVersionChangeEvent, requestIdentifier, requestedVersion);
+}
+
+void IDBConnectionProxy::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier)
+{
+ callConnectionOnMainThread(&IDBConnectionToServer::didFireVersionChangeEvent, databaseConnectionIdentifier, requestIdentifier);
+}
+
+void IDBConnectionProxy::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion)
+{
+ ASSERT(isMainThread());
+
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> locker(m_openDBRequestMapLock);
+ request = m_openDBRequestMap.get(requestIdentifier);
+ }
+
+ if (!request)
+ return;
+
+ request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestBlocked, oldVersion, newVersion);
+}
+
+void IDBConnectionProxy::openDBRequestCancelled(const IDBRequestData& requestData)
+{
+ callConnectionOnMainThread(&IDBConnectionToServer::openDBRequestCancelled, requestData);
+}
+
+void IDBConnectionProxy::establishTransaction(IDBTransaction& transaction)
+{
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ ASSERT(!hasRecordOfTransaction(transaction));
+ m_pendingTransactions.set(transaction.info().identifier(), &transaction);
+ }
+
+ callConnectionOnMainThread(&IDBConnectionToServer::establishTransaction, transaction.database().databaseConnectionIdentifier(), transaction.info());
+}
+
+void IDBConnectionProxy::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ RefPtr<IDBTransaction> transaction;
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ transaction = m_pendingTransactions.take(transactionIdentifier);
+ }
+
+ ASSERT(transaction);
+
+ transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didStart, error);
+}
+
+void IDBConnectionProxy::commitTransaction(IDBTransaction& transaction)
+{
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ ASSERT(!m_committingTransactions.contains(transaction.info().identifier()));
+ m_committingTransactions.set(transaction.info().identifier(), &transaction);
+ }
+
+ callConnectionOnMainThread(&IDBConnectionToServer::commitTransaction, transaction.info().identifier());
+}
+
+void IDBConnectionProxy::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ RefPtr<IDBTransaction> transaction;
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ transaction = m_committingTransactions.take(transactionIdentifier);
+ }
+
+ if (!transaction)
+ return;
+
+ transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didCommit, error);
+}
+
+void IDBConnectionProxy::abortTransaction(IDBTransaction& transaction)
+{
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ ASSERT(!m_abortingTransactions.contains(transaction.info().identifier()));
+ m_abortingTransactions.set(transaction.info().identifier(), &transaction);
+ }
+
+ callConnectionOnMainThread(&IDBConnectionToServer::abortTransaction, transaction.info().identifier());
+}
+
+void IDBConnectionProxy::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ RefPtr<IDBTransaction> transaction;
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ transaction = m_abortingTransactions.take(transactionIdentifier);
+ }
+
+ if (!transaction)
+ return;
+
+ transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didAbort, error);
+}
+
+bool IDBConnectionProxy::hasRecordOfTransaction(const IDBTransaction& transaction) const
+{
+ ASSERT(m_transactionMapLock.isLocked());
+
+ auto identifier = transaction.info().identifier();
+ return m_pendingTransactions.contains(identifier) || m_committingTransactions.contains(identifier) || m_abortingTransactions.contains(identifier);
+}
+
+void IDBConnectionProxy::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, IDBTransaction& transaction)
+{
+ callConnectionOnMainThread(&IDBConnectionToServer::didFinishHandlingVersionChangeTransaction, databaseConnectionIdentifier, transaction.info().identifier());
+}
+
+void IDBConnectionProxy::databaseConnectionPendingClose(IDBDatabase& database)
+{
+ callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionPendingClose, database.databaseConnectionIdentifier());
+}
+
+void IDBConnectionProxy::databaseConnectionClosed(IDBDatabase& database)
+{
+ callConnectionOnMainThread(&IDBConnectionToServer::databaseConnectionClosed, database.databaseConnectionIdentifier());
+}
+
+void IDBConnectionProxy::didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError& error)
+{
+ RefPtr<IDBDatabase> database;
+ {
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+ database = m_databaseConnectionMap.get(databaseConnectionIdentifier);
+ }
+
+ // If the IDBDatabase object is gone, message back to the server so it doesn't hang
+ // waiting for a reply that will never come.
+ if (!database) {
+ m_connectionToServer.confirmDidCloseFromServer(databaseConnectionIdentifier);
+ return;
+ }
+
+ database->performCallbackOnOriginThread(*database, &IDBDatabase::didCloseFromServer, error);
+}
+
+void IDBConnectionProxy::confirmDidCloseFromServer(IDBDatabase& database)
+{
+ callConnectionOnMainThread(&IDBConnectionToServer::confirmDidCloseFromServer, database.databaseConnectionIdentifier());
+}
+
+void IDBConnectionProxy::connectionToServerLost(const IDBError& error)
+{
+ Vector<uint64_t> databaseConnectionIdentifiers;
+ {
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+ copyKeysToVector(m_databaseConnectionMap, databaseConnectionIdentifiers);
+ }
+
+ for (auto connectionIdentifier : databaseConnectionIdentifiers) {
+ RefPtr<IDBDatabase> database;
+ {
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+ database = m_databaseConnectionMap.get(connectionIdentifier);
+ }
+
+ if (!database)
+ continue;
+
+ database->performCallbackOnOriginThread(*database, &IDBDatabase::connectionToServerLost, error);
+ }
+
+ Vector<IDBResourceIdentifier> openDBRequestIdentifiers;
+ {
+ Locker<Lock> locker(m_openDBRequestMapLock);
+ copyKeysToVector(m_openDBRequestMap, openDBRequestIdentifiers);
+ }
+
+ for (auto& requestIdentifier : openDBRequestIdentifiers) {
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> locker(m_openDBRequestMapLock);
+ request = m_openDBRequestMap.get(requestIdentifier);
+ }
+
+ if (!request)
+ continue;
+
+ auto result = IDBResultData::error(requestIdentifier, error);
+ request->performCallbackOnOriginThread(*request, &IDBOpenDBRequest::requestCompleted, result);
+ }
+}
+
+void IDBConnectionProxy::scheduleMainThreadTasks()
+{
+ Locker<Lock> locker(m_mainThreadTaskLock);
+ if (m_mainThreadProtector)
+ return;
+
+ m_mainThreadProtector = &m_connectionToServer;
+ callOnMainThread([this] {
+ handleMainThreadTasks();
+ });
+}
+
+void IDBConnectionProxy::handleMainThreadTasks()
+{
+ RefPtr<IDBConnectionToServer> protector;
+ {
+ Locker<Lock> locker(m_mainThreadTaskLock);
+ ASSERT(m_mainThreadProtector);
+ protector = WTFMove(m_mainThreadProtector);
+ }
+
+ while (auto task = m_mainThreadQueue.tryGetMessage())
+ task->performTask();
+}
+
+void IDBConnectionProxy::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function<void (const Vector<String>&)> callback)
+{
+ // This method is only meant to be called by the web inspector on the main thread.
+ RELEASE_ASSERT(isMainThread());
+
+ m_connectionToServer.getAllDatabaseNames(mainFrameOrigin, openingOrigin, callback);
+}
+
+void IDBConnectionProxy::registerDatabaseConnection(IDBDatabase& database)
+{
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+
+ ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier()));
+ m_databaseConnectionMap.set(database.databaseConnectionIdentifier(), &database);
+}
+
+void IDBConnectionProxy::unregisterDatabaseConnection(IDBDatabase& database)
+{
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+
+ ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier()) || m_databaseConnectionMap.get(database.databaseConnectionIdentifier()) == &database);
+ m_databaseConnectionMap.remove(database.databaseConnectionIdentifier());
+}
+
+void IDBConnectionProxy::forgetActiveOperations(const Vector<RefPtr<TransactionOperation>>& operations)
+{
+ Locker<Lock> locker(m_transactionOperationLock);
+
+ for (auto& operation : operations)
+ m_activeOperations.remove(operation->identifier());
+}
+
+template<typename KeyType, typename ValueType>
+void removeItemsMatchingCurrentThread(HashMap<KeyType, ValueType>& map)
+{
+ auto currentThreadID = currentThread();
+
+ Vector<KeyType> keys;
+ keys.reserveInitialCapacity(map.size());
+ for (auto& iterator : map) {
+ if (iterator.value->originThreadID() == currentThreadID)
+ keys.uncheckedAppend(iterator.key);
+ }
+
+ for (auto& key : keys)
+ map.remove(key);
+}
+
+void IDBConnectionProxy::forgetActivityForCurrentThread()
+{
+ ASSERT(!isMainThread());
+
+ {
+ Locker<Lock> lock(m_databaseConnectionMapLock);
+ removeItemsMatchingCurrentThread(m_databaseConnectionMap);
+ }
+ {
+ Locker<Lock> lock(m_openDBRequestMapLock);
+ removeItemsMatchingCurrentThread(m_openDBRequestMap);
+ }
+ {
+ Locker<Lock> lock(m_transactionMapLock);
+ removeItemsMatchingCurrentThread(m_pendingTransactions);
+ removeItemsMatchingCurrentThread(m_committingTransactions);
+ removeItemsMatchingCurrentThread(m_abortingTransactions);
+ }
+ {
+ Locker<Lock> lock(m_transactionOperationLock);
+ removeItemsMatchingCurrentThread(m_activeOperations);
+ }
+}
+
+} // namesapce IDBClient
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h
new file mode 100644
index 000000000..5c01ac34c
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToServer.h"
+#include "IDBResourceIdentifier.h"
+#include "TransactionOperation.h"
+#include <functional>
+#include <wtf/CrossThreadQueue.h>
+#include <wtf/CrossThreadTask.h>
+#include <wtf/HashMap.h>
+#include <wtf/MainThread.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class IDBDatabase;
+class IDBDatabaseIdentifier;
+class IDBError;
+class IDBOpenDBRequest;
+class IDBResultData;
+class IDBTransaction;
+class ScriptExecutionContext;
+class SecurityOrigin;
+
+struct IDBGetRecordData;
+struct IDBIterateCursorData;
+
+namespace IDBClient {
+
+class IDBConnectionToServer;
+
+class IDBConnectionProxy {
+public:
+ IDBConnectionProxy(IDBConnectionToServer&);
+
+ Ref<IDBOpenDBRequest> openDatabase(ScriptExecutionContext&, const IDBDatabaseIdentifier&, uint64_t version);
+ void didOpenDatabase(const IDBResultData&);
+
+ Ref<IDBOpenDBRequest> deleteDatabase(ScriptExecutionContext&, const IDBDatabaseIdentifier&);
+ void didDeleteDatabase(const IDBResultData&);
+
+ void createObjectStore(TransactionOperation&, const IDBObjectStoreInfo&);
+ void deleteObjectStore(TransactionOperation&, const String& objectStoreName);
+ void clearObjectStore(TransactionOperation&, uint64_t objectStoreIdentifier);
+ void createIndex(TransactionOperation&, const IDBIndexInfo&);
+ void deleteIndex(TransactionOperation&, uint64_t objectStoreIdentifier, const String& indexName);
+ void putOrAdd(TransactionOperation&, IDBKeyData&&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode);
+ void getRecord(TransactionOperation&, const IDBGetRecordData&);
+ void getAllRecords(TransactionOperation&, const IDBGetAllRecordsData&);
+ void getCount(TransactionOperation&, const IDBKeyRangeData&);
+ void deleteRecord(TransactionOperation&, const IDBKeyRangeData&);
+ void openCursor(TransactionOperation&, const IDBCursorInfo&);
+ void iterateCursor(TransactionOperation&, const IDBIterateCursorData&);
+ void renameObjectStore(TransactionOperation&, uint64_t objectStoreIdentifier, const String& newName);
+ void renameIndex(TransactionOperation&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName);
+
+ void fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion);
+ void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier);
+
+ void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion);
+ void openDBRequestCancelled(const IDBRequestData&);
+
+ void establishTransaction(IDBTransaction&);
+ void commitTransaction(IDBTransaction&);
+ void abortTransaction(IDBTransaction&);
+
+ void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+ void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+ void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+
+ void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, IDBTransaction&);
+ void databaseConnectionPendingClose(IDBDatabase&);
+ void databaseConnectionClosed(IDBDatabase&);
+
+ void didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError&);
+ void confirmDidCloseFromServer(IDBDatabase&);
+
+ void connectionToServerLost(const IDBError&);
+
+ void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier);
+
+ void completeOperation(const IDBResultData&);
+
+ uint64_t serverConnectionIdentifier() const { return m_serverConnectionIdentifier; }
+
+ void ref();
+ void deref();
+
+ void getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function<void (const Vector<String>&)>);
+
+ void registerDatabaseConnection(IDBDatabase&);
+ void unregisterDatabaseConnection(IDBDatabase&);
+
+ void forgetActiveOperations(const Vector<RefPtr<TransactionOperation>>&);
+ void forgetActivityForCurrentThread();
+
+private:
+ void completeOpenDBRequest(const IDBResultData&);
+ bool hasRecordOfTransaction(const IDBTransaction&) const;
+
+ void saveOperation(TransactionOperation&);
+
+ template<typename... Parameters, typename... Arguments>
+ void callConnectionOnMainThread(void (IDBConnectionToServer::*method)(Parameters...), Arguments&&... arguments)
+ {
+ if (isMainThread())
+ (m_connectionToServer.*method)(std::forward<Arguments>(arguments)...);
+ else
+ postMainThreadTask(m_connectionToServer, method, arguments...);
+ }
+
+ template<typename... Arguments>
+ void postMainThreadTask(Arguments&&... arguments)
+ {
+ auto task = createCrossThreadTask(arguments...);
+ m_mainThreadQueue.append(WTFMove(task));
+
+ scheduleMainThreadTasks();
+ }
+
+ void scheduleMainThreadTasks();
+ void handleMainThreadTasks();
+
+ IDBConnectionToServer& m_connectionToServer;
+ uint64_t m_serverConnectionIdentifier;
+
+ HashMap<uint64_t, IDBDatabase*> m_databaseConnectionMap;
+ Lock m_databaseConnectionMapLock;
+
+ HashMap<IDBResourceIdentifier, RefPtr<IDBOpenDBRequest>> m_openDBRequestMap;
+ Lock m_openDBRequestMapLock;
+
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_pendingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_committingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_abortingTransactions;
+ Lock m_transactionMapLock;
+
+ HashMap<IDBResourceIdentifier, RefPtr<TransactionOperation>> m_activeOperations;
+ Lock m_transactionOperationLock;
+
+ CrossThreadQueue<CrossThreadTask> m_mainThreadQueue;
+ Lock m_mainThreadTaskLock;
+ RefPtr<IDBConnectionToServer> m_mainThreadProtector;
+};
+
+} // namespace IDBClient
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp
new file mode 100644
index 000000000..75c5773a5
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBConnectionToServer.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionProxy.h"
+#include "IDBDatabase.h"
+#include "IDBGetRecordData.h"
+#include "IDBKeyRangeData.h"
+#include "IDBOpenDBRequest.h"
+#include "IDBRequestData.h"
+#include "IDBResultData.h"
+#include "Logging.h"
+#include "TransactionOperation.h"
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+namespace IDBClient {
+
+Ref<IDBConnectionToServer> IDBConnectionToServer::create(IDBConnectionToServerDelegate& delegate)
+{
+ return adoptRef(*new IDBConnectionToServer(delegate));
+}
+
+IDBConnectionToServer::IDBConnectionToServer(IDBConnectionToServerDelegate& delegate)
+ : m_delegate(delegate)
+ , m_proxy(std::make_unique<IDBConnectionProxy>(*this))
+{
+}
+
+uint64_t IDBConnectionToServer::identifier() const
+{
+ return m_delegate->identifier();
+}
+
+IDBConnectionProxy& IDBConnectionToServer::proxy()
+{
+ ASSERT(m_proxy);
+ return *m_proxy;
+}
+
+void IDBConnectionToServer::deleteDatabase(const IDBRequestData& request)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::deleteDatabase - %s", request.databaseIdentifier().debugString().utf8().data());
+ m_delegate->deleteDatabase(request);
+}
+
+void IDBConnectionToServer::didDeleteDatabase(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didDeleteDatabase");
+ m_proxy->didDeleteDatabase(resultData);
+}
+
+void IDBConnectionToServer::openDatabase(const IDBRequestData& request)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::openDatabase - %s (%s) (%" PRIu64 ")", request.databaseIdentifier().debugString().utf8().data(), request.requestIdentifier().loggingString().utf8().data(), request.requestedVersion());
+ m_delegate->openDatabase(request);
+}
+
+void IDBConnectionToServer::didOpenDatabase(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didOpenDatabase");
+ m_proxy->didOpenDatabase(resultData);
+}
+
+void IDBConnectionToServer::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::createObjectStore");
+ ASSERT(isMainThread());
+
+ m_delegate->createObjectStore(requestData, info);
+}
+
+void IDBConnectionToServer::didCreateObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didCreateObjectStore");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::deleteObjectStore");
+ ASSERT(isMainThread());
+
+ m_delegate->deleteObjectStore(requestData, objectStoreName);
+}
+
+void IDBConnectionToServer::didDeleteObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didDeleteObjectStore");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::renameObjectStore");
+ ASSERT(isMainThread());
+
+ m_delegate->renameObjectStore(requestData, objectStoreIdentifier, newName);
+}
+
+void IDBConnectionToServer::didRenameObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didRenameObjectStore");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::clearObjectStore");
+ ASSERT(isMainThread());
+
+ m_delegate->clearObjectStore(requestData, objectStoreIdentifier);
+}
+
+void IDBConnectionToServer::didClearObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didClearObjectStore");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::createIndex");
+ ASSERT(isMainThread());
+
+ m_delegate->createIndex(requestData, info);
+}
+
+void IDBConnectionToServer::didCreateIndex(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didCreateIndex");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::deleteIndex");
+ ASSERT(isMainThread());
+
+ m_delegate->deleteIndex(requestData, objectStoreIdentifier, indexName);
+}
+
+void IDBConnectionToServer::didDeleteIndex(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didDeleteIndex");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::renameIndex");
+ ASSERT(isMainThread());
+
+ m_delegate->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName);
+}
+
+void IDBConnectionToServer::didRenameIndex(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didRenameIndex");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& key, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode overwriteMode)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::putOrAdd");
+ ASSERT(isMainThread());
+
+ m_delegate->putOrAdd(requestData, key, value, overwriteMode);
+}
+
+void IDBConnectionToServer::didPutOrAdd(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didPutOrAdd");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::getRecord");
+ ASSERT(isMainThread());
+ ASSERT(!getRecordData.keyRangeData.isNull);
+
+ m_delegate->getRecord(requestData, getRecordData);
+}
+
+void IDBConnectionToServer::didGetRecord(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didGetRecord");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::getAllRecords");
+ ASSERT(isMainThread());
+
+ m_delegate->getAllRecords(requestData, getAllRecordsData);
+}
+
+void IDBConnectionToServer::didGetAllRecords(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didGetAllRecords");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::getCount");
+ ASSERT(isMainThread());
+ ASSERT(!keyRangeData.isNull);
+
+ m_delegate->getCount(requestData, keyRangeData);
+}
+
+void IDBConnectionToServer::didGetCount(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didGetCount");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::deleteRecord");
+ ASSERT(isMainThread());
+ ASSERT(!keyRangeData.isNull);
+
+ m_delegate->deleteRecord(requestData, keyRangeData);
+}
+
+void IDBConnectionToServer::didDeleteRecord(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didDeleteRecord");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::openCursor");
+ ASSERT(isMainThread());
+
+ m_delegate->openCursor(requestData, info);
+}
+
+void IDBConnectionToServer::didOpenCursor(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didOpenCursor");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::iterateCursor");
+ ASSERT(isMainThread());
+
+ m_delegate->iterateCursor(requestData, data);
+}
+
+void IDBConnectionToServer::didIterateCursor(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didIterateCursor");
+ m_proxy->completeOperation(resultData);
+}
+
+void IDBConnectionToServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::establishTransaction");
+ ASSERT(isMainThread());
+
+ m_delegate->establishTransaction(databaseConnectionIdentifier, info);
+}
+
+void IDBConnectionToServer::commitTransaction(const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::commitTransaction");
+ ASSERT(isMainThread());
+
+ m_delegate->commitTransaction(transactionIdentifier);
+}
+
+void IDBConnectionToServer::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didCommitTransaction");
+ ASSERT(isMainThread());
+
+ m_proxy->didCommitTransaction(transactionIdentifier, error);
+}
+
+void IDBConnectionToServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didFinishHandlingVersionChangeTransaction");
+ ASSERT(isMainThread());
+
+ m_delegate->didFinishHandlingVersionChangeTransaction(databaseConnectionIdentifier, transactionIdentifier);
+}
+
+void IDBConnectionToServer::abortTransaction(const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::abortTransaction");
+ ASSERT(isMainThread());
+
+ m_delegate->abortTransaction(transactionIdentifier);
+}
+
+void IDBConnectionToServer::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didAbortTransaction");
+ ASSERT(isMainThread());
+
+ m_proxy->didAbortTransaction(transactionIdentifier, error);
+}
+
+void IDBConnectionToServer::fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::fireVersionChangeEvent");
+ ASSERT(isMainThread());
+
+ m_proxy->fireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier, requestedVersion);
+}
+
+void IDBConnectionToServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didFireVersionChangeEvent");
+ ASSERT(isMainThread());
+
+ m_delegate->didFireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier);
+}
+
+void IDBConnectionToServer::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didStartTransaction");
+ ASSERT(isMainThread());
+
+ m_proxy->didStartTransaction(transactionIdentifier, error);
+}
+
+void IDBConnectionToServer::didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError& error)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didCloseFromServer");
+ ASSERT(isMainThread());
+
+ m_proxy->didCloseFromServer(databaseConnectionIdentifier, error);
+}
+
+void IDBConnectionToServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::confirmDidCloseFromServer");
+ ASSERT(isMainThread());
+
+ m_delegate->confirmDidCloseFromServer(databaseConnectionIdentifier);
+}
+
+void IDBConnectionToServer::connectionToServerLost(const IDBError& error)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::connectionToServerLost");
+ ASSERT(isMainThread());
+
+ m_proxy->connectionToServerLost(error);
+}
+
+void IDBConnectionToServer::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::didStartTransaction");
+ ASSERT(isMainThread());
+
+ m_proxy->notifyOpenDBRequestBlocked(requestIdentifier, oldVersion, newVersion);
+}
+
+void IDBConnectionToServer::openDBRequestCancelled(const IDBRequestData& requestData)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::openDBRequestCancelled");
+ ASSERT(isMainThread());
+
+ m_delegate->openDBRequestCancelled(requestData);
+}
+
+void IDBConnectionToServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::databaseConnectionPendingClose");
+ ASSERT(isMainThread());
+
+ m_delegate->databaseConnectionPendingClose(databaseConnectionIdentifier);
+}
+
+void IDBConnectionToServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::databaseConnectionClosed");
+ ASSERT(isMainThread());
+
+ m_delegate->databaseConnectionClosed(databaseConnectionIdentifier);
+}
+
+void IDBConnectionToServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBConnectionToServer::abortOpenAndUpgradeNeeded");
+ ASSERT(isMainThread());
+
+ m_delegate->abortOpenAndUpgradeNeeded(databaseConnectionIdentifier, transactionIdentifier);
+}
+
+void IDBConnectionToServer::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function<void (const Vector<String>&)> callback)
+{
+ static uint64_t callbackID = 0;
+
+ m_getAllDatabaseNamesCallbacks.add(++callbackID, WTFMove(callback));
+
+ m_delegate->getAllDatabaseNames(SecurityOriginData::fromSecurityOrigin(mainFrameOrigin), SecurityOriginData::fromSecurityOrigin(openingOrigin), callbackID);
+}
+
+void IDBConnectionToServer::didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames)
+{
+ auto callback = m_getAllDatabaseNamesCallbacks.take(callbackID);
+ ASSERT(callback);
+
+ callback(databaseNames);
+}
+
+} // namespace IDBClient
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h
new file mode 100644
index 000000000..7651f4305
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionProxy.h"
+#include "IDBConnectionToServerDelegate.h"
+#include "IDBResourceIdentifier.h"
+#include <wtf/HashMap.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBDatabase;
+class IDBError;
+class IDBObjectStoreInfo;
+class IDBResultData;
+class IDBValue;
+class SecurityOrigin;
+
+struct IDBGetAllRecordsData;
+struct IDBGetRecordData;
+struct IDBIterateCursorData;
+
+namespace IDBClient {
+
+class IDBConnectionToServer : public ThreadSafeRefCounted<IDBConnectionToServer> {
+public:
+ WEBCORE_EXPORT static Ref<IDBConnectionToServer> create(IDBConnectionToServerDelegate&);
+
+ uint64_t identifier() const;
+
+ IDBConnectionProxy& proxy();
+
+ void deleteDatabase(const IDBRequestData&);
+ WEBCORE_EXPORT void didDeleteDatabase(const IDBResultData&);
+
+ void openDatabase(const IDBRequestData&);
+ WEBCORE_EXPORT void didOpenDatabase(const IDBResultData&);
+
+ void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&);
+ WEBCORE_EXPORT void didCreateObjectStore(const IDBResultData&);
+
+ void deleteObjectStore(const IDBRequestData&, const String& objectStoreName);
+ WEBCORE_EXPORT void didDeleteObjectStore(const IDBResultData&);
+
+ void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName);
+ WEBCORE_EXPORT void didRenameObjectStore(const IDBResultData&);
+
+ void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier);
+ WEBCORE_EXPORT void didClearObjectStore(const IDBResultData&);
+
+ void createIndex(const IDBRequestData&, const IDBIndexInfo&);
+ WEBCORE_EXPORT void didCreateIndex(const IDBResultData&);
+
+ void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName);
+ WEBCORE_EXPORT void didDeleteIndex(const IDBResultData&);
+
+ void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName);
+ WEBCORE_EXPORT void didRenameIndex(const IDBResultData&);
+
+ void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode);
+ WEBCORE_EXPORT void didPutOrAdd(const IDBResultData&);
+
+ void getRecord(const IDBRequestData&, const IDBGetRecordData&);
+ WEBCORE_EXPORT void didGetRecord(const IDBResultData&);
+
+ void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&);
+ WEBCORE_EXPORT void didGetAllRecords(const IDBResultData&);
+
+ void getCount(const IDBRequestData&, const IDBKeyRangeData&);
+ WEBCORE_EXPORT void didGetCount(const IDBResultData&);
+
+ void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&);
+ WEBCORE_EXPORT void didDeleteRecord(const IDBResultData&);
+
+ void openCursor(const IDBRequestData&, const IDBCursorInfo&);
+ WEBCORE_EXPORT void didOpenCursor(const IDBResultData&);
+
+ void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&);
+ WEBCORE_EXPORT void didIterateCursor(const IDBResultData&);
+
+ void commitTransaction(const IDBResourceIdentifier& transactionIdentifier);
+ WEBCORE_EXPORT void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+
+ void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&);
+
+ void abortTransaction(const IDBResourceIdentifier& transactionIdentifier);
+ WEBCORE_EXPORT void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+
+ WEBCORE_EXPORT void fireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion);
+ void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier);
+
+ WEBCORE_EXPORT void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+
+ WEBCORE_EXPORT void didCloseFromServer(uint64_t databaseConnectionIdentifier, const IDBError&);
+ void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier);
+
+ WEBCORE_EXPORT void connectionToServerLost(const IDBError&);
+
+ WEBCORE_EXPORT void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion);
+ void openDBRequestCancelled(const IDBRequestData&);
+
+ void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&);
+
+ void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier);
+ void databaseConnectionClosed(uint64_t databaseConnectionIdentifier);
+
+ // To be used when an IDBOpenDBRequest gets a new database connection, optionally with a
+ // versionchange transaction, but the page is already torn down.
+ void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier);
+
+ void getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function<void (const Vector<String>&)>);
+ WEBCORE_EXPORT void didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames);
+
+private:
+ IDBConnectionToServer(IDBConnectionToServerDelegate&);
+
+ Ref<IDBConnectionToServerDelegate> m_delegate;
+
+ HashMap<uint64_t, std::function<void (const Vector<String>&)>> m_getAllDatabaseNamesCallbacks;
+
+ std::unique_ptr<IDBConnectionProxy> m_proxy;
+};
+
+} // namespace IDBClient
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h
new file mode 100644
index 000000000..a61c283a2
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBIndexInfo;
+class IDBKeyData;
+class IDBObjectStoreInfo;
+class IDBRequestData;
+class IDBResourceIdentifier;
+class IDBTransactionInfo;
+class IDBValue;
+
+struct IDBGetAllRecordsData;
+struct IDBGetRecordData;
+struct IDBIterateCursorData;
+struct SecurityOriginData;
+
+namespace IndexedDB {
+enum class ObjectStoreOverwriteMode;
+}
+
+struct IDBKeyRangeData;
+
+namespace IDBClient {
+
+class IDBConnectionToServerDelegate {
+public:
+ virtual ~IDBConnectionToServerDelegate() { }
+
+ virtual uint64_t identifier() const = 0;
+ virtual void deleteDatabase(const IDBRequestData&) = 0;
+ virtual void openDatabase(const IDBRequestData&) = 0;
+ virtual void abortTransaction(const IDBResourceIdentifier&) = 0;
+ virtual void commitTransaction(const IDBResourceIdentifier&) = 0;
+ virtual void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&) = 0;
+ virtual void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&) = 0;
+ virtual void deleteObjectStore(const IDBRequestData&, const String& objectStoreName) = 0;
+ virtual void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName) = 0;
+ virtual void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier) = 0;
+ virtual void createIndex(const IDBRequestData&, const IDBIndexInfo&) = 0;
+ virtual void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName) = 0;
+ virtual void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) = 0;
+ virtual void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode) = 0;
+ virtual void getRecord(const IDBRequestData&, const IDBGetRecordData&) = 0;
+ virtual void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&) = 0;
+ virtual void getCount(const IDBRequestData&, const IDBKeyRangeData&) = 0;
+ virtual void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&) = 0;
+ virtual void openCursor(const IDBRequestData&, const IDBCursorInfo&) = 0;
+ virtual void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&) = 0;
+
+ virtual void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&) = 0;
+ virtual void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier) = 0;
+ virtual void databaseConnectionClosed(uint64_t databaseConnectionIdentifier) = 0;
+ virtual void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) = 0;
+ virtual void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) = 0;
+ virtual void openDBRequestCancelled(const IDBRequestData&) = 0;
+ virtual void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier) = 0;
+
+ virtual void getAllDatabaseNames(const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID) = 0;
+
+ virtual void ref() = 0;
+ virtual void deref() = 0;
+};
+
+} // namespace IDBClient
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp
new file mode 100644
index 000000000..8f3547522
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "TransactionOperation.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursor.h"
+#include <heap/HeapInlines.h>
+
+namespace WebCore {
+namespace IDBClient {
+
+TransactionOperation::TransactionOperation(IDBTransaction& transaction, IDBRequest& request)
+ : TransactionOperation(transaction)
+{
+ m_objectStoreIdentifier = request.sourceObjectStoreIdentifier();
+ m_indexIdentifier = request.sourceIndexIdentifier();
+ if (m_indexIdentifier)
+ m_indexRecordType = request.requestedIndexRecordType();
+ if (auto* cursor = request.pendingCursor())
+ m_cursorIdentifier = std::make_unique<IDBResourceIdentifier>(cursor->info().identifier());
+
+ m_idbRequest = &request;
+}
+
+} // namespace IDBClient
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h
new file mode 100644
index 000000000..cd95befd5
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/client/TransactionOperation.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBRequest.h"
+#include "IDBRequestData.h"
+#include "IDBResourceIdentifier.h"
+#include "IDBResultData.h"
+#include "IDBTransaction.h"
+#include <wtf/MainThread.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class IDBResultData;
+
+namespace IndexedDB {
+enum class IndexRecordType;
+}
+
+namespace IDBClient {
+
+class TransactionOperation : public ThreadSafeRefCounted<TransactionOperation> {
+ friend IDBRequestData::IDBRequestData(TransactionOperation&);
+public:
+ virtual ~TransactionOperation()
+ {
+ ASSERT(m_originThreadID == currentThread());
+ }
+
+ void perform()
+ {
+ ASSERT(m_originThreadID == currentThread());
+ ASSERT(m_performFunction);
+ m_performFunction();
+ m_performFunction = { };
+ }
+
+ void transitionToCompleteOnThisThread(const IDBResultData& data)
+ {
+ ASSERT(m_originThreadID == currentThread());
+ m_transaction->operationCompletedOnServer(data, *this);
+ }
+
+ void transitionToComplete(const IDBResultData& data, RefPtr<TransactionOperation>&& lastRef)
+ {
+ ASSERT(isMainThread());
+
+ if (m_originThreadID == currentThread())
+ transitionToCompleteOnThisThread(data);
+ else {
+ m_transaction->performCallbackOnOriginThread(*this, &TransactionOperation::transitionToCompleteOnThisThread, data);
+ m_transaction->callFunctionOnOriginThread([lastRef = WTFMove(lastRef)]() {
+ });
+ }
+ }
+
+ void doComplete(const IDBResultData& data)
+ {
+ ASSERT(m_originThreadID == currentThread());
+
+ // Due to race conditions between the server sending an "operation complete" message and the client
+ // forcefully aborting an operation, it's unavoidable that this method might be called twice.
+ // It's okay to handle that gracefully with an early return.
+ if (!m_completeFunction)
+ return;
+
+ m_completeFunction(data);
+ m_transaction->operationCompletedOnClient(*this);
+
+ // m_completeFunction might be holding the last ref to this TransactionOperation,
+ // so we need to do this trick to null it out without first destroying it.
+ std::function<void (const IDBResultData&)> oldCompleteFunction;
+ std::swap(m_completeFunction, oldCompleteFunction);
+ }
+
+ const IDBResourceIdentifier& identifier() const { return m_identifier; }
+
+ ThreadIdentifier originThreadID() const { return m_originThreadID; }
+
+ IDBRequest* idbRequest() { return m_idbRequest.get(); }
+
+ bool nextRequestCanGoToServer() const { return m_nextRequestCanGoToServer && m_idbRequest; }
+ void setNextRequestCanGoToServer(bool nextRequestCanGoToServer) { m_nextRequestCanGoToServer = nextRequestCanGoToServer; }
+
+protected:
+ TransactionOperation(IDBTransaction& transaction)
+ : m_transaction(transaction)
+ , m_identifier(transaction.connectionProxy())
+ {
+ }
+
+ TransactionOperation(IDBTransaction&, IDBRequest&);
+
+ Ref<IDBTransaction> m_transaction;
+ IDBResourceIdentifier m_identifier;
+ uint64_t m_objectStoreIdentifier { 0 };
+ uint64_t m_indexIdentifier { 0 };
+ std::unique_ptr<IDBResourceIdentifier> m_cursorIdentifier;
+ IndexedDB::IndexRecordType m_indexRecordType;
+ std::function<void ()> m_performFunction;
+ std::function<void (const IDBResultData&)> m_completeFunction;
+
+private:
+ IDBResourceIdentifier transactionIdentifier() const { return m_transaction->info().identifier(); }
+ uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; }
+ uint64_t indexIdentifier() const { return m_indexIdentifier; }
+ IDBResourceIdentifier* cursorIdentifier() const { return m_cursorIdentifier.get(); }
+ IDBTransaction& transaction() { return m_transaction.get(); }
+ IndexedDB::IndexRecordType indexRecordType() const { return m_indexRecordType; }
+
+ ThreadIdentifier m_originThreadID { currentThread() };
+ RefPtr<IDBRequest> m_idbRequest;
+ bool m_nextRequestCanGoToServer { true };
+};
+
+template <typename... Arguments>
+class TransactionOperationImpl final : public TransactionOperation {
+public:
+ TransactionOperationImpl(IDBTransaction& transaction, void (IDBTransaction::*completeMethod)(const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments)
+ : TransactionOperation(transaction)
+ {
+ RefPtr<TransactionOperation> protectedThis(this);
+
+ ASSERT(performMethod);
+ m_performFunction = [protectedThis, this, performMethod, arguments...] {
+ (&m_transaction.get()->*performMethod)(*this, arguments...);
+ };
+
+ if (completeMethod) {
+ m_completeFunction = [protectedThis, this, completeMethod](const IDBResultData& resultData) {
+ if (completeMethod)
+ (&m_transaction.get()->*completeMethod)(resultData);
+ };
+ }
+ }
+
+ TransactionOperationImpl(IDBTransaction& transaction, IDBRequest& request, void (IDBTransaction::*completeMethod)(IDBRequest&, const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments)
+ : TransactionOperation(transaction, request)
+ {
+ RefPtr<TransactionOperation> protectedThis(this);
+
+ ASSERT(performMethod);
+ m_performFunction = [protectedThis, this, performMethod, arguments...] {
+ (&m_transaction.get()->*performMethod)(*this, arguments...);
+ };
+
+ if (completeMethod) {
+ RefPtr<IDBRequest> refRequest(&request);
+ m_completeFunction = [protectedThis, this, refRequest, completeMethod](const IDBResultData& resultData) {
+ if (completeMethod)
+ (&m_transaction.get()->*completeMethod)(*refRequest, resultData);
+ };
+ }
+ }
+};
+
+inline RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ void (IDBTransaction::*complete)(const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&))
+{
+ auto operation = new TransactionOperationImpl<>(transaction, complete, perform);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ void (IDBTransaction::*complete)(const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1),
+ const P1& parameter1)
+{
+ auto operation = new TransactionOperationImpl<MP1>(transaction, complete, perform, parameter1);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1, typename MP2, typename P2>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ void (IDBTransaction::*complete)(const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2),
+ const P1& parameter1,
+ const P2& parameter2)
+{
+ auto operation = new TransactionOperationImpl<MP1, MP2>(transaction, complete, perform, parameter1, parameter2);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1, typename MP2, typename P2, typename MP3, typename P3>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ void (IDBTransaction::*complete)(const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3),
+ const P1& parameter1,
+ const P2& parameter2,
+ const P3& parameter3)
+{
+ auto operation = new TransactionOperationImpl<MP1, MP2, MP3>(transaction, complete, perform, parameter1, parameter2, parameter3);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ IDBRequest& request,
+ void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1),
+ const P1& parameter1)
+{
+ auto operation = new TransactionOperationImpl<MP1>(transaction, request, complete, perform, parameter1);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1, typename MP2, typename P2>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ IDBRequest& request,
+ void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2),
+ const P1& parameter1,
+ const P2& parameter2)
+{
+ auto operation = new TransactionOperationImpl<MP1, MP2>(transaction, request, complete, perform, parameter1, parameter2);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename MP2, typename MP3, typename P1, typename P2, typename P3>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ IDBRequest& request,
+ void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3),
+ const P1& parameter1,
+ const P2& parameter2,
+ const P3& parameter3)
+{
+ auto operation = new TransactionOperationImpl<MP1, MP2, MP3>(transaction, request, complete, perform, parameter1, parameter2, parameter3);
+ return adoptRef(operation);
+}
+
+} // namespace IDBClient
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp
deleted file mode 100644
index b3c5f6113..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBBackingStoreCursorLevelDB.h"
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#include "IDBBackingStoreLevelDB.h"
-#include "LevelDBTransaction.h"
-
-namespace WebCore {
-
-IDBBackingStoreCursorLevelDB::IDBBackingStoreCursorLevelDB(const IDBBackingStoreCursorLevelDB* other)
- : m_transaction(other->m_transaction)
- , m_cursorOptions(other->m_cursorOptions)
- , m_currentKey(other->m_currentKey)
- , m_recordIdentifier(IDBRecordIdentifier::create())
-{
- if (other->m_iterator) {
- m_iterator = m_transaction->createIterator();
-
- if (other->m_iterator->isValid()) {
- m_iterator->seek(other->m_iterator->key());
- ASSERT(m_iterator->isValid());
- }
- }
-
- m_recordIdentifier->reset(other->m_recordIdentifier->encodedPrimaryKey(), other->m_recordIdentifier->version());
-}
-
-bool IDBBackingStoreCursorLevelDB::firstSeek()
-{
- m_iterator = m_transaction->createIterator();
- if (m_cursorOptions.forward)
- m_iterator->seek(m_cursorOptions.lowKey);
- else
- m_iterator->seek(m_cursorOptions.highKey);
-
- return continueFunction(0, Ready);
-}
-
-bool IDBBackingStoreCursorLevelDB::advance(unsigned long count)
-{
- while (count--) {
- if (!continueFunction())
- return false;
- }
- return true;
-}
-
-bool IDBBackingStoreCursorLevelDB::continueFunction(const IDBKey* key, IteratorState nextState)
-{
- RefPtr<IDBKey> previousKey = m_currentKey;
-
- bool firstIteration = true;
-
- // When iterating with PrevNoDuplicate, spec requires that the
- // value we yield for each key is the first duplicate in forwards
- // order.
- RefPtr<IDBKey> lastDuplicateKey;
-
- bool forward = m_cursorOptions.forward;
-
- for (;;) {
- if (nextState == Seek) {
- // FIXME: Optimize seeking for reverse cursors as well.
- if (firstIteration && key && forward) {
- m_iterator->seek(encodeKey(*key));
- firstIteration = false;
- } else if (forward)
- m_iterator->next();
- else
- m_iterator->prev();
- } else
- nextState = Seek; // for subsequent iterations
-
- if (!m_iterator->isValid()) {
- if (!forward && lastDuplicateKey.get()) {
- // We need to walk forward because we hit the end of
- // the data.
- forward = true;
- continue;
- }
-
- return false;
- }
-
- if (isPastBounds()) {
- if (!forward && lastDuplicateKey.get()) {
- // We need to walk forward because now we're beyond the
- // bounds defined by the cursor.
- forward = true;
- continue;
- }
-
- return false;
- }
-
- if (!haveEnteredRange())
- continue;
-
- // The row may not load because there's a stale entry in the
- // index. This is not fatal.
- if (!loadCurrentRow())
- continue;
-
- if (key) {
- if (forward) {
- if (m_currentKey->isLessThan(key))
- continue;
- } else {
- if (key->isLessThan(m_currentKey.get()))
- continue;
- }
- }
-
- if (m_cursorOptions.unique) {
-
- if (m_currentKey->isEqual(previousKey.get())) {
- // We should never be able to walk forward all the way
- // to the previous key.
- ASSERT(!lastDuplicateKey.get());
- continue;
- }
-
- if (!forward) {
- if (!lastDuplicateKey.get()) {
- lastDuplicateKey = m_currentKey;
- continue;
- }
-
- // We need to walk forward because we hit the boundary
- // between key ranges.
- if (!lastDuplicateKey->isEqual(m_currentKey.get())) {
- forward = true;
- continue;
- }
-
- continue;
- }
- }
- break;
- }
-
- ASSERT(!lastDuplicateKey.get() || (forward && lastDuplicateKey->isEqual(m_currentKey.get())));
- return true;
-}
-
-bool IDBBackingStoreCursorLevelDB::haveEnteredRange() const
-{
- if (m_cursorOptions.forward) {
- if (m_cursorOptions.lowOpen)
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) > 0;
-
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) >= 0;
- }
- if (m_cursorOptions.highOpen)
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) < 0;
-
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) <= 0;
-}
-
-bool IDBBackingStoreCursorLevelDB::isPastBounds() const
-{
- if (m_cursorOptions.forward) {
- if (m_cursorOptions.highOpen)
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) >= 0;
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) > 0;
- }
-
- if (m_cursorOptions.lowOpen)
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) <= 0;
- return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) < 0;
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h
deleted file mode 100644
index 972088478..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBBackingStoreCursorLevelDB_h
-#define IDBBackingStoreCursorLevelDB_h
-
-#include "IDBKey.h"
-#include "IDBRecordIdentifier.h"
-#include "LevelDBIterator.h"
-
-#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
-
-namespace WebCore {
-
-class LevelDBTransaction;
-class SharedBuffer;
-
-class IDBBackingStoreCursorLevelDB : public RefCounted<IDBBackingStoreCursorLevelDB> {
-public:
- enum IteratorState {
- Ready = 0,
- Seek
- };
-
- struct CursorOptions {
- int64_t databaseId;
- int64_t objectStoreId;
- int64_t indexId;
- Vector<char> lowKey;
- bool lowOpen;
- Vector<char> highKey;
- bool highOpen;
- bool forward;
- bool unique;
- };
-
- virtual PassRefPtr<IDBKey> key() const { return m_currentKey; }
- virtual bool continueFunction(const IDBKey* = 0, IteratorState = Seek);
- virtual bool advance(unsigned long);
- bool firstSeek();
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone() = 0;
-
- virtual PassRefPtr<IDBKey> primaryKey() const { return m_currentKey; }
- virtual PassRefPtr<SharedBuffer> value() const = 0;
- virtual const IDBRecordIdentifier& recordIdentifier() const { return *m_recordIdentifier; }
- virtual ~IDBBackingStoreCursorLevelDB() { }
- virtual bool loadCurrentRow() = 0;
-
-protected:
- IDBBackingStoreCursorLevelDB(int64_t cursorID, LevelDBTransaction* transaction, const CursorOptions& cursorOptions)
- : m_cursorID(cursorID)
- , m_transaction(transaction)
- , m_cursorOptions(cursorOptions)
- , m_recordIdentifier(IDBRecordIdentifier::create())
- {
- }
- explicit IDBBackingStoreCursorLevelDB(const IDBBackingStoreCursorLevelDB* other);
-
- virtual Vector<char> encodeKey(const IDBKey&) = 0;
-
- bool isPastBounds() const;
- bool haveEnteredRange() const;
-
- int64_t m_cursorID;
- LevelDBTransaction* m_transaction;
- const CursorOptions m_cursorOptions;
- OwnPtr<LevelDBIterator> m_iterator;
- RefPtr<IDBKey> m_currentKey;
- RefPtr<IDBRecordIdentifier> m_recordIdentifier;
-};
-
-} // namespace WebCore
-
-#endif // USE(LEVELDB)
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBBackingStoreCursorLevelDB_h
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp
deleted file mode 100644
index 0712bdf38..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp
+++ /dev/null
@@ -1,1931 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 "IDBBackingStoreLevelDB.h"
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#include "FileSystem.h"
-#include "HistogramSupport.h"
-#include "IDBDatabaseMetadata.h"
-#include "IDBIndexWriterLevelDB.h"
-#include "IDBKey.h"
-#include "IDBKeyPath.h"
-#include "IDBKeyRange.h"
-#include "IDBLevelDBCoding.h"
-#include "IDBTransactionBackend.h"
-#include "LevelDBComparator.h"
-#include "LevelDBDatabase.h"
-#include "LevelDBIterator.h"
-#include "LevelDBSlice.h"
-#include "LevelDBTransaction.h"
-#include "Logging.h"
-#include "SecurityOrigin.h"
-#include "SharedBuffer.h"
-#include <wtf/Assertions.h>
-
-namespace WebCore {
-
-using namespace IDBLevelDBCoding;
-
-const int64_t KeyGeneratorInitialNumber = 1; // From the IndexedDB specification.
-
-enum IDBBackingStoreLevelDBErrorSource {
- // 0 - 2 are no longer used.
- FindKeyInIndex = 3,
- GetIDBDatabaseMetaData,
- GetIndexes,
- GetKeyGeneratorCurrentNumber,
- GetObjectStores,
- GetRecord,
- KeyExistsInObjectStore,
- LoadCurrentRow,
- SetupMetadata,
- GetPrimaryKeyViaIndex,
- KeyExistsInIndex,
- VersionExists,
- DeleteObjectStore,
- SetMaxObjectStoreId,
- SetMaxIndexId,
- GetNewDatabaseId,
- GetNewVersionNumber,
- CreateIDBDatabaseMetaData,
- DeleteDatabase,
- IDBLevelDBBackingStoreInternalErrorMax,
-};
-
-static void recordInternalError(const char* type, IDBBackingStoreLevelDBErrorSource location)
-{
- String name = String::format("WebCore.IndexedDB.BackingStore.%sError", type);
- HistogramSupport::histogramEnumeration(name.utf8().data(), location, IDBLevelDBBackingStoreInternalErrorMax);
-}
-
-// Use to signal conditions that usually indicate developer error, but could be caused by data corruption.
-// A macro is used instead of an inline function so that the assert and log report the line number.
-#define REPORT_ERROR(type, location) \
- do { \
- LOG_ERROR("IndexedDB %s Error: %s", type, #location); \
- ASSERT_NOT_REACHED(); \
- recordInternalError(type, location); \
- } while (0)
-
-#define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
-#define INTERNAL_CONSISTENCY_ERROR(location) REPORT_ERROR("Consistency", location)
-#define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
-
-static void putBool(LevelDBTransaction* transaction, const LevelDBSlice& key, bool value)
-{
- transaction->put(key, encodeBool(value));
-}
-
-template <typename DBOrTransaction>
-static bool getInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found)
-{
- Vector<char> result;
- bool ok = db->safeGet(key, result, found);
- if (!ok)
- return false;
- if (!found)
- return true;
-
- foundInt = decodeInt(result.begin(), result.end());
- return true;
-}
-
-static void putInt(LevelDBTransaction* transaction, const LevelDBSlice& key, int64_t value)
-{
- ASSERT(value >= 0);
- transaction->put(key, encodeInt(value));
-}
-
-template <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getVarInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found)
-{
- Vector<char> result;
- bool ok = db->safeGet(key, result, found);
- if (!ok)
- return false;
- if (!found)
- return true;
-
- found = decodeVarInt(result.begin(), result.end(), foundInt) == result.end();
- return true;
-}
-
-static void putVarInt(LevelDBTransaction* transaction, const LevelDBSlice& key, int64_t value)
-{
- transaction->put(key, encodeVarInt(value));
-}
-
-template <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getString(DBOrTransaction* db, const LevelDBSlice& key, String& foundString, bool& found)
-{
- Vector<char> result;
- found = false;
- bool ok = db->safeGet(key, result, found);
- if (!ok)
- return false;
- if (!found)
- return true;
-
- foundString = decodeString(result.begin(), result.end());
- return true;
-}
-
-static void putString(LevelDBTransaction* transaction, const LevelDBSlice& key, const String& value)
-{
- transaction->put(key, encodeString(value));
-}
-
-static void putIDBKeyPath(LevelDBTransaction* transaction, const LevelDBSlice& key, const IDBKeyPath& value)
-{
- transaction->put(key, encodeIDBKeyPath(value));
-}
-
-static int compareKeys(const LevelDBSlice& a, const LevelDBSlice& b)
-{
- return compare(a, b);
-}
-
-int IDBBackingStoreLevelDB::compareIndexKeys(const LevelDBSlice& a, const LevelDBSlice& b)
-{
- return compare(a, b, true);
-}
-
-class Comparator : public LevelDBComparator {
-public:
- virtual int compare(const LevelDBSlice& a, const LevelDBSlice& b) const { return IDBLevelDBCoding::compare(a, b); }
- virtual const char* name() const { return "idb_cmp1"; }
-};
-
-// 0 - Initial version.
-// 1 - Adds UserIntVersion to DatabaseMetaData.
-// 2 - Adds DataVersion to to global metadata.
-const int64_t latestKnownSchemaVersion = 2;
-WARN_UNUSED_RETURN static bool isSchemaKnown(LevelDBDatabase* db, bool& known)
-{
- int64_t dbSchemaVersion = 0;
- bool found = false;
- bool ok = getInt(db, SchemaVersionKey::encode(), dbSchemaVersion, found);
- if (!ok)
- return false;
- if (!found) {
- known = true;
- return true;
- }
- if (dbSchemaVersion > latestKnownSchemaVersion) {
- known = false;
- return true;
- }
-
- const uint32_t latestKnownDataVersion = SerializedScriptValue::wireFormatVersion();
- int64_t dbDataVersion = 0;
- ok = getInt(db, DataVersionKey::encode(), dbDataVersion, found);
- if (!ok)
- return false;
- if (!found) {
- known = true;
- return true;
- }
-
- if (dbDataVersion > latestKnownDataVersion) {
- known = false;
- return true;
- }
-
- known = true;
- return true;
-}
-
-WARN_UNUSED_RETURN static bool setUpMetadata(LevelDBDatabase* db, const String& origin)
-{
- const uint32_t latestKnownDataVersion = SerializedScriptValue::wireFormatVersion();
- const Vector<char> schemaVersionKey = SchemaVersionKey::encode();
- const Vector<char> dataVersionKey = DataVersionKey::encode();
-
- RefPtr<LevelDBTransaction> transaction = LevelDBTransaction::create(db);
-
- int64_t dbSchemaVersion = 0;
- int64_t dbDataVersion = 0;
- bool found = false;
- bool ok = getInt(transaction.get(), schemaVersionKey, dbSchemaVersion, found);
- if (!ok) {
- INTERNAL_READ_ERROR(SetupMetadata);
- return false;
- }
- if (!found) {
- // Initialize new backing store.
- dbSchemaVersion = latestKnownSchemaVersion;
- putInt(transaction.get(), schemaVersionKey, dbSchemaVersion);
- dbDataVersion = latestKnownDataVersion;
- putInt(transaction.get(), dataVersionKey, dbDataVersion);
- } else {
- // Upgrade old backing store.
- ASSERT(dbSchemaVersion <= latestKnownSchemaVersion);
- if (dbSchemaVersion < 1) {
- dbSchemaVersion = 1;
- putInt(transaction.get(), schemaVersionKey, dbSchemaVersion);
- const Vector<char> startKey = DatabaseNameKey::encodeMinKeyForOrigin(origin);
- const Vector<char> stopKey = DatabaseNameKey::encodeStopKeyForOrigin(origin);
- OwnPtr<LevelDBIterator> it = db->createIterator();
- for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) {
- int64_t databaseId = 0;
- found = false;
- bool ok = getInt(transaction.get(), it->key(), databaseId, found);
- if (!ok) {
- INTERNAL_READ_ERROR(SetupMetadata);
- return false;
- }
- if (!found) {
- INTERNAL_CONSISTENCY_ERROR(SetupMetadata);
- return false;
- }
- Vector<char> intVersionKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::UserIntVersion);
- putVarInt(transaction.get(), intVersionKey, IDBDatabaseMetadata::DefaultIntVersion);
- }
- }
- if (dbSchemaVersion < 2) {
- dbSchemaVersion = 2;
- putInt(transaction.get(), schemaVersionKey, dbSchemaVersion);
- dbDataVersion = SerializedScriptValue::wireFormatVersion();
- putInt(transaction.get(), dataVersionKey, dbDataVersion);
- }
- }
-
- // All new values will be written using this serialization version.
- found = false;
- ok = getInt(transaction.get(), dataVersionKey, dbDataVersion, found);
- if (!ok) {
- INTERNAL_READ_ERROR(SetupMetadata);
- return false;
- }
- if (!found) {
- INTERNAL_CONSISTENCY_ERROR(SetupMetadata);
- return false;
- }
- if (dbDataVersion < latestKnownDataVersion) {
- dbDataVersion = latestKnownDataVersion;
- putInt(transaction.get(), dataVersionKey, dbDataVersion);
- }
-
- ASSERT(dbSchemaVersion == latestKnownSchemaVersion);
- ASSERT(dbDataVersion == latestKnownDataVersion);
-
- if (!transaction->commit()) {
- INTERNAL_WRITE_ERROR(SetupMetadata);
- return false;
- }
- return true;
-}
-
-template <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, int64_t databaseId, int64_t& maxObjectStoreId)
-{
- const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::MaxObjectStoreId);
- bool ok = getMaxObjectStoreId(db, maxObjectStoreIdKey, maxObjectStoreId);
- return ok;
-}
-
-template <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, const Vector<char>& maxObjectStoreIdKey, int64_t& maxObjectStoreId)
-{
- maxObjectStoreId = -1;
- bool found = false;
- bool ok = getInt(db, maxObjectStoreIdKey, maxObjectStoreId, found);
- if (!ok)
- return false;
- if (!found)
- maxObjectStoreId = 0;
-
- ASSERT(maxObjectStoreId >= 0);
- return true;
-}
-
-class DefaultLevelDBFactory : public LevelDBFactory {
-public:
- virtual PassOwnPtr<LevelDBDatabase> openLevelDB(const String& fileName, const LevelDBComparator* comparator)
- {
- return LevelDBDatabase::open(fileName, comparator);
- }
- virtual bool destroyLevelDB(const String& fileName)
- {
- return LevelDBDatabase::destroy(fileName);
- }
-};
-
-IDBBackingStoreLevelDB::IDBBackingStoreLevelDB(const String& identifier, PassOwnPtr<LevelDBDatabase> db, PassOwnPtr<LevelDBComparator> comparator)
- : m_identifier(identifier)
- , m_db(db)
- , m_comparator(comparator)
- , m_weakFactory(this)
-{
-}
-
-IDBBackingStoreLevelDB::~IDBBackingStoreLevelDB()
-{
- // m_db's destructor uses m_comparator. The order of destruction is important.
- m_db.clear();
- m_comparator.clear();
-}
-
-enum IDBLevelDBBackingStoreOpenResult {
- IDBLevelDBBackingStoreOpenMemorySuccess,
- IDBLevelDBBackingStoreOpenSuccess,
- IDBLevelDBBackingStoreOpenFailedDirectory,
- IDBLevelDBBackingStoreOpenFailedUnknownSchema,
- IDBLevelDBBackingStoreOpenCleanupDestroyFailed,
- IDBLevelDBBackingStoreOpenCleanupReopenFailed,
- IDBLevelDBBackingStoreOpenCleanupReopenSuccess,
- IDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema,
- IDBLevelDBBackingStoreOpenFailedUnknownErr,
- IDBLevelDBBackingStoreOpenMemoryFailed,
- IDBLevelDBBackingStoreOpenAttemptNonASCII,
- IDBLevelDBBackingStoreOpenMax,
-};
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier)
-{
- DefaultLevelDBFactory levelDBFactory;
- return IDBBackingStoreLevelDB::open(securityOrigin, pathBaseArg, fileIdentifier, &levelDBFactory);
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier, LevelDBFactory* levelDBFactory)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::open");
- ASSERT(!pathBaseArg.isEmpty());
- String pathBase = pathBaseArg;
-
- OwnPtr<LevelDBComparator> comparator = adoptPtr(new Comparator());
- OwnPtr<LevelDBDatabase> db;
-
- if (!pathBase.containsOnlyASCII())
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenAttemptNonASCII, IDBLevelDBBackingStoreOpenMax);
- if (!makeAllDirectories(pathBase)) {
- LOG_ERROR("Unable to create IndexedDB database path %s", pathBase.utf8().data());
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedDirectory, IDBLevelDBBackingStoreOpenMax);
- return PassRefPtr<IDBBackingStoreLevelDB>();
- }
-
- String path = pathByAppendingComponent(pathBase, securityOrigin.databaseIdentifier() + ".indexeddb.leveldb");
-
- db = levelDBFactory->openLevelDB(path, comparator.get());
- if (db) {
- bool known = false;
- bool ok = isSchemaKnown(db.get(), known);
- if (!ok) {
- LOG_ERROR("IndexedDB had IO error checking schema, treating it as failure to open");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedIOErrCheckingSchema, IDBLevelDBBackingStoreOpenMax);
- db.clear();
- } else if (!known) {
- LOG_ERROR("IndexedDB backing store had unknown schema, treating it as failure to open");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedUnknownSchema, IDBLevelDBBackingStoreOpenMax);
- db.clear();
- }
- }
-
- if (db)
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenSuccess, IDBLevelDBBackingStoreOpenMax);
- else {
- LOG_ERROR("IndexedDB backing store open failed, attempting cleanup");
- bool success = levelDBFactory->destroyLevelDB(path);
- if (!success) {
- LOG_ERROR("IndexedDB backing store cleanup failed");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupDestroyFailed, IDBLevelDBBackingStoreOpenMax);
- return PassRefPtr<IDBBackingStoreLevelDB>();
- }
-
- LOG_ERROR("IndexedDB backing store cleanup succeeded, reopening");
- db = levelDBFactory->openLevelDB(path, comparator.get());
- if (!db) {
- LOG_ERROR("IndexedDB backing store reopen after recovery failed");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupReopenFailed, IDBLevelDBBackingStoreOpenMax);
- return PassRefPtr<IDBBackingStoreLevelDB>();
- }
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupReopenSuccess, IDBLevelDBBackingStoreOpenMax);
- }
-
- if (!db) {
- ASSERT_NOT_REACHED();
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedUnknownErr, IDBLevelDBBackingStoreOpenMax);
- return PassRefPtr<IDBBackingStoreLevelDB>();
- }
-
- return create(fileIdentifier, db.release(), comparator.release());
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::openInMemory(const String& identifier)
-{
- DefaultLevelDBFactory levelDBFactory;
- return IDBBackingStoreLevelDB::openInMemory(identifier, &levelDBFactory);
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::openInMemory(const String& identifier, LevelDBFactory*)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::openInMemory");
-
- OwnPtr<LevelDBComparator> comparator = adoptPtr(new Comparator());
- OwnPtr<LevelDBDatabase> db = LevelDBDatabase::openInMemory(comparator.get());
- if (!db) {
- LOG_ERROR("LevelDBDatabase::openInMemory failed.");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemoryFailed, IDBLevelDBBackingStoreOpenMax);
- return PassRefPtr<IDBBackingStoreLevelDB>();
- }
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemorySuccess, IDBLevelDBBackingStoreOpenMax);
-
- return create(identifier, db.release(), comparator.release());
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::create(const String& identifier, PassOwnPtr<LevelDBDatabase> db, PassOwnPtr<LevelDBComparator> comparator)
-{
- // FIXME: Handle comparator name changes.
- RefPtr<IDBBackingStoreLevelDB> backingStore(adoptRef(new IDBBackingStoreLevelDB(identifier, db, comparator)));
-
- if (!setUpMetadata(backingStore->m_db.get(), identifier))
- return PassRefPtr<IDBBackingStoreLevelDB>();
-
- return backingStore.release();
-}
-
-Vector<String> IDBBackingStoreLevelDB::getDatabaseNames()
-{
- Vector<String> foundNames;
- const Vector<char> startKey = DatabaseNameKey::encodeMinKeyForOrigin(m_identifier);
- const Vector<char> stopKey = DatabaseNameKey::encodeStopKeyForOrigin(m_identifier);
-
- ASSERT(foundNames.isEmpty());
-
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
- for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) {
- const char* p = it->key().begin();
- const char* limit = it->key().end();
-
- DatabaseNameKey databaseNameKey;
- p = DatabaseNameKey::decode(p, limit, &databaseNameKey);
- ASSERT(p);
-
- foundNames.append(databaseNameKey.databaseName());
- }
- return foundNames;
-}
-
-bool IDBBackingStoreLevelDB::getIDBDatabaseMetaData(const String& name, IDBDatabaseMetadata* metadata, bool& found)
-{
- const Vector<char> key = DatabaseNameKey::encode(m_identifier, name);
- found = false;
-
- bool ok = getInt(m_db.get(), key, metadata->id, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
- return false;
- }
- if (!found)
- return true;
-
- // FIXME: The string version is no longer supported, so the levelDB ports should consider refactoring off of it.
- String stringVersion;
- ok = getString(m_db.get(), DatabaseMetaDataKey::encode(metadata->id, DatabaseMetaDataKey::UserVersion), stringVersion, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
- return false;
- }
- if (!found) {
- INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData);
- return false;
- }
-
- int64_t version;
- ok = getVarInt(m_db.get(), DatabaseMetaDataKey::encode(metadata->id, DatabaseMetaDataKey::UserIntVersion), version, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
- return false;
- }
- if (!found) {
- INTERNAL_CONSISTENCY_ERROR(GetIDBDatabaseMetaData);
- return false;
- }
-
- // 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;
- metadata->version = version;
-
- ok = getMaxObjectStoreId(m_db.get(), metadata->id, metadata->maxObjectStoreId);
- if (!ok) {
- INTERNAL_READ_ERROR(GetIDBDatabaseMetaData);
- return false;
- }
-
- return true;
-}
-
-void IDBBackingStoreLevelDB::getOrEstablishIDBDatabaseMetadata(const String& name, std::function<void (const IDBDatabaseMetadata&, bool success)> 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);
-
- newId = -1;
- int64_t maxDatabaseId = -1;
- bool found = false;
- bool ok = getInt(transaction.get(), MaxDatabaseIdKey::encode(), maxDatabaseId, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetNewDatabaseId);
- return false;
- }
- if (!found)
- maxDatabaseId = 0;
-
- ASSERT(maxDatabaseId >= 0);
-
- int64_t databaseId = maxDatabaseId + 1;
- putInt(transaction.get(), MaxDatabaseIdKey::encode(), databaseId);
- if (!transaction->commit()) {
- INTERNAL_WRITE_ERROR(GetNewDatabaseId);
- return false;
- }
- newId = databaseId;
- return true;
-}
-
-// FIXME: LevelDB needs to support uint64_t as the version type.
-bool IDBBackingStoreLevelDB::createIDBDatabaseMetaData(IDBDatabaseMetadata& metadata)
-{
- bool ok = getNewDatabaseId(m_db.get(), metadata.id);
- if (!ok)
- return false;
- ASSERT(metadata.id >= 0);
-
- RefPtr<LevelDBTransaction> transaction = LevelDBTransaction::create(m_db.get());
- 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;
- }
- return true;
-}
-
-bool IDBBackingStoreLevelDB::updateIDBDatabaseVersion(IDBBackingStoreTransactionLevelDB& transaction, int64_t rowId, uint64_t version)
-{
- if (version == IDBDatabaseMetadata::NoIntVersion)
- version = IDBDatabaseMetadata::DefaultIntVersion;
- putVarInt(IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::UserIntVersion), version);
- return true;
-}
-
-static void deleteRange(LevelDBTransaction* transaction, const Vector<char>& begin, const Vector<char>& end)
-{
- OwnPtr<LevelDBIterator> it = transaction->createIterator();
- for (it->seek(begin); it->isValid() && compareKeys(it->key(), end) < 0; it->next())
- transaction->remove(it->key());
-}
-
-void IDBBackingStoreLevelDB::deleteDatabase(const String& name, std::function<void (bool success)> boolCallbackFunction)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteDatabase");
- OwnPtr<LevelDBWriteOnlyTransaction> transaction = LevelDBWriteOnlyTransaction::create(m_db.get());
-
- IDBDatabaseMetadata metadata;
- bool success = false;
- bool ok = getIDBDatabaseMetaData(name, &metadata, success);
- if (!ok) {
- boolCallbackFunction(false);
- return;
- }
-
- if (!success) {
- boolCallbackFunction(true);
- return;
- }
-
- const Vector<char> startKey = DatabaseMetaDataKey::encode(metadata.id, DatabaseMetaDataKey::OriginName);
- const Vector<char> stopKey = DatabaseMetaDataKey::encode(metadata.id + 1, DatabaseMetaDataKey::OriginName);
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
- for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next())
- transaction->remove(it->key());
-
- const Vector<char> key = DatabaseNameKey::encode(m_identifier, name);
- transaction->remove(key);
-
- if (!transaction->commit()) {
- INTERNAL_WRITE_ERROR(DeleteDatabase);
- boolCallbackFunction(false);
- return;
- }
- boolCallbackFunction(true);
-}
-
-static bool checkObjectStoreAndMetaDataType(const LevelDBIterator* it, const Vector<char>& stopKey, int64_t objectStoreId, int64_t metaDataType)
-{
- if (!it->isValid() || compareKeys(it->key(), stopKey) >= 0)
- return false;
-
- ObjectStoreMetaDataKey metaDataKey;
- const char* p = ObjectStoreMetaDataKey::decode(it->key().begin(), it->key().end(), &metaDataKey);
- ASSERT_UNUSED(p, p);
- if (metaDataKey.objectStoreId() != objectStoreId)
- return false;
- if (metaDataKey.metaDataType() != metaDataType)
- return false;
- return true;
-}
-
-// FIXME: This should do some error handling rather than plowing ahead when bad data is encountered.
-bool IDBBackingStoreLevelDB::getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::getObjectStores");
- if (!KeyPrefix::isValidDatabaseId(databaseId))
- return false;
- const Vector<char> startKey = ObjectStoreMetaDataKey::encode(databaseId, 1, 0);
- const Vector<char> stopKey = ObjectStoreMetaDataKey::encodeMaxKey(databaseId);
-
- ASSERT(objectStores->isEmpty());
-
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
- it->seek(startKey);
- while (it->isValid() && compareKeys(it->key(), stopKey) < 0) {
- const char* p = it->key().begin();
- const char* limit = it->key().end();
-
- ObjectStoreMetaDataKey metaDataKey;
- p = ObjectStoreMetaDataKey::decode(p, limit, &metaDataKey);
- ASSERT(p);
- if (metaDataKey.metaDataType() != ObjectStoreMetaDataKey::Name) {
- INTERNAL_CONSISTENCY_ERROR(GetObjectStores);
- // Possible stale metadata, but don't fail the load.
- it->next();
- continue;
- }
-
- int64_t objectStoreId = metaDataKey.objectStoreId();
-
- // FIXME: Do this by direct key lookup rather than iteration, to simplify.
- String objectStoreName = decodeString(it->value().begin(), it->value().end());
-
- it->next();
- if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::KeyPath)) {
- INTERNAL_CONSISTENCY_ERROR(GetObjectStores);
- break;
- }
- IDBKeyPath keyPath = decodeIDBKeyPath(it->value().begin(), it->value().end());
-
- it->next();
- if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::AutoIncrement)) {
- INTERNAL_CONSISTENCY_ERROR(GetObjectStores);
- break;
- }
- bool autoIncrement = decodeBool(it->value().begin(), it->value().end());
-
- it->next(); // Is evicatble.
- if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::Evictable)) {
- INTERNAL_CONSISTENCY_ERROR(GetObjectStores);
- break;
- }
-
- it->next(); // Last version.
- if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::LastVersion)) {
- INTERNAL_CONSISTENCY_ERROR(GetObjectStores);
- break;
- }
-
- it->next(); // Maximum index id allocated.
- if (!checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId)) {
- INTERNAL_CONSISTENCY_ERROR(GetObjectStores);
- break;
- }
- int64_t maxIndexId = decodeInt(it->value().begin(), it->value().end());
-
- it->next(); // [optional] has key path (is not null)
- if (checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::HasKeyPath)) {
- bool hasKeyPath = decodeBool(it->value().begin(), it->value().end());
- // This check accounts for two layers of legacy coding:
- // (1) Initially, hasKeyPath was added to distinguish null vs. string.
- // (2) Later, null vs. string vs. array was stored in the keyPath itself.
- // So this check is only relevant for string-type keyPaths.
- if (!hasKeyPath && (keyPath.type() == IDBKeyPath::StringType && !keyPath.string().isEmpty())) {
- INTERNAL_CONSISTENCY_ERROR(GetObjectStores);
- break;
- }
- if (!hasKeyPath)
- keyPath = IDBKeyPath();
- it->next();
- }
-
- int64_t keyGeneratorCurrentNumber = -1;
- if (checkObjectStoreAndMetaDataType(it.get(), stopKey, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber)) {
- keyGeneratorCurrentNumber = decodeInt(it->value().begin(), it->value().end());
- // FIXME: Return keyGeneratorCurrentNumber, cache in object store, and write lazily to backing store.
- // For now, just assert that if it was written it was valid.
- ASSERT_UNUSED(keyGeneratorCurrentNumber, keyGeneratorCurrentNumber >= KeyGeneratorInitialNumber);
- it->next();
- }
-
- IDBObjectStoreMetadata metadata(objectStoreName, objectStoreId, keyPath, autoIncrement, maxIndexId);
- if (!getIndexes(databaseId, objectStoreId, &metadata.indexes))
- return false;
- objectStores->set(objectStoreId, metadata);
- }
- return true;
-}
-
-WARN_UNUSED_RETURN static bool setMaxObjectStoreId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId)
-{
- const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::MaxObjectStoreId);
- int64_t maxObjectStoreId = -1;
- bool ok = getMaxObjectStoreId(transaction, maxObjectStoreIdKey, maxObjectStoreId);
- if (!ok) {
- INTERNAL_READ_ERROR(SetMaxObjectStoreId);
- return false;
- }
-
- if (objectStoreId <= maxObjectStoreId) {
- INTERNAL_CONSISTENCY_ERROR(SetMaxObjectStoreId);
- return false;
- }
- putInt(transaction, maxObjectStoreIdKey, objectStoreId);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::createObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::createObjectStore");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- if (!setMaxObjectStoreId(levelDBTransaction, databaseId, objectStoreId))
- return false;
-
- const Vector<char> nameKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Name);
- const Vector<char> keyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyPath);
- const Vector<char> autoIncrementKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::AutoIncrement);
- const Vector<char> evictableKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Evictable);
- const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::LastVersion);
- const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId);
- const Vector<char> hasKeyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::HasKeyPath);
- const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber);
- const Vector<char> namesKey = ObjectStoreNamesKey::encode(databaseId, name);
-
- putString(levelDBTransaction, nameKey, name);
- putIDBKeyPath(levelDBTransaction, keyPathKey, keyPath);
- putInt(levelDBTransaction, autoIncrementKey, autoIncrement);
- putInt(levelDBTransaction, evictableKey, false);
- putInt(levelDBTransaction, lastVersionKey, 1);
- putInt(levelDBTransaction, maxIndexIdKey, MinimumIndexId);
- putBool(levelDBTransaction, hasKeyPathKey, !keyPath.isNull());
- putInt(levelDBTransaction, keyGeneratorCurrentNumberKey, KeyGeneratorInitialNumber);
- putInt(levelDBTransaction, namesKey, objectStoreId);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::deleteObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteObjectStore");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
-
- String objectStoreName;
- bool found = false;
- bool ok = getString(levelDBTransaction, ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Name), objectStoreName, found);
- if (!ok) {
- INTERNAL_READ_ERROR(DeleteObjectStore);
- return false;
- }
- if (!found) {
- INTERNAL_CONSISTENCY_ERROR(DeleteObjectStore);
- return false;
- }
-
- deleteRange(levelDBTransaction, ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), ObjectStoreMetaDataKey::encodeMaxKey(databaseId, objectStoreId));
-
- levelDBTransaction->remove(ObjectStoreNamesKey::encode(databaseId, objectStoreName));
-
- deleteRange(levelDBTransaction, IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encodeMaxKey(databaseId, objectStoreId));
- deleteRange(levelDBTransaction, IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId));
-
- return clearObjectStore(transaction, databaseId, objectStoreId);
-}
-
-bool IDBBackingStoreLevelDB::getRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, Vector<char>& record)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::getRecord");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
-
- const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
- Vector<char> data;
-
- record.clear();
-
- bool found = false;
- bool ok = levelDBTransaction->safeGet(leveldbKey, data, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetRecord);
- return false;
- }
- if (!found)
- return true;
-
- int64_t version;
- const char* p = decodeVarInt(data.begin(), data.end(), version);
- if (!p) {
- INTERNAL_READ_ERROR(GetRecord);
- return false;
- }
-
- record.appendRange(p, static_cast<const char*>(data.end()));
- return true;
-}
-
-WARN_UNUSED_RETURN static bool getNewVersionNumber(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t& newVersionNumber)
-{
- const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::LastVersion);
-
- newVersionNumber = -1;
- int64_t lastVersion = -1;
- bool found = false;
- bool ok = getInt(transaction, lastVersionKey, lastVersion, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetNewVersionNumber);
- return false;
- }
- if (!found)
- lastVersion = 0;
-
- ASSERT(lastVersion >= 0);
-
- int64_t version = lastVersion + 1;
- putInt(transaction, lastVersionKey, version);
-
- ASSERT(version > lastVersion); // FIXME: Think about how we want to handle the overflow scenario.
-
- newVersionNumber = version;
- return true;
-}
-
-bool IDBBackingStoreLevelDB::putRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, PassRefPtr<SharedBuffer> prpValue, IDBRecordIdentifier* recordIdentifier)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::putRecord");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- ASSERT(key.isValid());
-
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- int64_t version = -1;
- bool ok = getNewVersionNumber(levelDBTransaction, databaseId, objectStoreId, version);
- if (!ok)
- return false;
- ASSERT(version >= 0);
- const Vector<char> objectStoredataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
-
- Vector<char> v;
- v.appendVector(encodeVarInt(version));
- RefPtr<SharedBuffer> value = prpValue;
- ASSERT(value);
- v.append(value->data(), value->size());
-
- levelDBTransaction->put(objectStoredataKey, v);
-
- const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, key);
- levelDBTransaction->put(existsEntryKey, encodeInt(version));
-
- recordIdentifier->reset(encodeIDBKey(key), version);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::clearObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::clearObjectStore");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- const Vector<char> startKey = KeyPrefix(databaseId, objectStoreId).encode();
- const Vector<char> stopKey = KeyPrefix(databaseId, objectStoreId + 1).encode();
-
- deleteRange(levelDBTransaction, startKey, stopKey);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::deleteRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBRecordIdentifier& recordIdentifier)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteRecord");
-
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
-
- const Vector<char> objectStoreDataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, recordIdentifier.encodedPrimaryKey());
- levelDBTransaction->remove(objectStoreDataKey);
-
- const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, recordIdentifier.encodedPrimaryKey());
- levelDBTransaction->remove(existsEntryKey);
- return true;
-}
-
-
-bool IDBBackingStoreLevelDB::getKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t& keyGeneratorCurrentNumber)
-{
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
-
- const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber);
-
- keyGeneratorCurrentNumber = -1;
- Vector<char> data;
-
- bool found = false;
- bool ok = levelDBTransaction->safeGet(keyGeneratorCurrentNumberKey, data, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetKeyGeneratorCurrentNumber);
- return false;
- }
- if (found)
- keyGeneratorCurrentNumber = decodeInt(data.begin(), data.end());
- else {
- // Previously, the key generator state was not stored explicitly but derived from the
- // maximum numeric key present in existing data. This violates the spec as the data may
- // be cleared but the key generator state must be preserved.
- const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
- const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
-
- OwnPtr<LevelDBIterator> it = levelDBTransaction->createIterator();
- int64_t maxNumericKey = 0;
-
- for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) {
- const char* p = it->key().begin();
- const char* limit = it->key().end();
-
- ObjectStoreDataKey dataKey;
- p = ObjectStoreDataKey::decode(p, limit, &dataKey);
- ASSERT(p);
-
- if (dataKey.userKey()->type() == IDBKey::NumberType) {
- int64_t n = static_cast<int64_t>(dataKey.userKey()->number());
- if (n > maxNumericKey)
- maxNumericKey = n;
- }
- }
-
- keyGeneratorCurrentNumber = maxNumericKey + 1;
- }
-
- return keyGeneratorCurrentNumber;
-}
-
-bool IDBBackingStoreLevelDB::maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t newNumber, bool checkCurrent)
-{
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
-
- if (checkCurrent) {
- int64_t currentNumber;
- bool ok = getKeyGeneratorCurrentNumber(transaction, databaseId, objectStoreId, currentNumber);
- if (!ok)
- return false;
- if (newNumber <= currentNumber)
- return true;
- }
-
- const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber);
- putInt(levelDBTransaction, keyGeneratorCurrentNumberKey, newNumber);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::keyExistsInObjectStore(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, RefPtr<IDBRecordIdentifier>& foundIDBRecordIdentifier)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInObjectStore");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- bool found = false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
- Vector<char> data;
-
- bool ok = levelDBTransaction->safeGet(leveldbKey, data, found);
- if (!ok) {
- INTERNAL_READ_ERROR(KeyExistsInObjectStore);
- return false;
- }
- if (!found)
- return true;
-
- int64_t version;
- if (!decodeVarInt(data.begin(), data.end(), version))
- return false;
-
- foundIDBRecordIdentifier = IDBRecordIdentifier::create(encodeIDBKey(key), version);
- return true;
-}
-
-static bool checkIndexAndMetaDataKey(const LevelDBIterator* it, const Vector<char>& stopKey, int64_t indexId, unsigned char metaDataType)
-{
- if (!it->isValid() || compareKeys(it->key(), stopKey) >= 0)
- return false;
-
- IndexMetaDataKey metaDataKey;
- const char* p = IndexMetaDataKey::decode(it->key().begin(), it->key().end(), &metaDataKey);
- ASSERT_UNUSED(p, p);
- if (metaDataKey.indexId() != indexId)
- return false;
- if (metaDataKey.metaDataType() != metaDataType)
- return false;
- return true;
-}
-
-
-// FIXME: This should do some error handling rather than plowing ahead when bad data is encountered.
-bool IDBBackingStoreLevelDB::getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap* indexes)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::getIndexes");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- const Vector<char> startKey = IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0);
- const Vector<char> stopKey = IndexMetaDataKey::encode(databaseId, objectStoreId + 1, 0, 0);
-
- ASSERT(indexes->isEmpty());
-
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
- it->seek(startKey);
- while (it->isValid() && compareKeys(it->key(), stopKey) < 0) {
- const char* p = it->key().begin();
- const char* limit = it->key().end();
-
- IndexMetaDataKey metaDataKey;
- p = IndexMetaDataKey::decode(p, limit, &metaDataKey);
- ASSERT(p);
- if (metaDataKey.metaDataType() != IndexMetaDataKey::Name) {
- INTERNAL_CONSISTENCY_ERROR(GetIndexes);
- // Possible stale metadata due to http://webkit.org/b/85557 but don't fail the load.
- it->next();
- continue;
- }
-
- // FIXME: Do this by direct key lookup rather than iteration, to simplify.
- int64_t indexId = metaDataKey.indexId();
- String indexName = decodeString(it->value().begin(), it->value().end());
-
- it->next(); // unique flag
- if (!checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::Unique)) {
- INTERNAL_CONSISTENCY_ERROR(GetIndexes);
- break;
- }
- bool indexUnique = decodeBool(it->value().begin(), it->value().end());
-
- it->next(); // keyPath
- if (!checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::KeyPath)) {
- INTERNAL_CONSISTENCY_ERROR(GetIndexes);
- break;
- }
- IDBKeyPath keyPath = decodeIDBKeyPath(it->value().begin(), it->value().end());
-
- it->next(); // [optional] multiEntry flag
- bool indexMultiEntry = false;
- if (checkIndexAndMetaDataKey(it.get(), stopKey, indexId, IndexMetaDataKey::MultiEntry)) {
- indexMultiEntry = decodeBool(it->value().begin(), it->value().end());
- it->next();
- }
-
- indexes->set(indexId, IDBIndexMetadata(indexName, indexId, keyPath, indexUnique, indexMultiEntry));
- }
- return true;
-}
-
-WARN_UNUSED_RETURN static bool setMaxIndexId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- int64_t maxIndexId = -1;
- const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId);
- bool found = false;
- bool ok = getInt(transaction, maxIndexIdKey, maxIndexId, found);
- if (!ok) {
- INTERNAL_READ_ERROR(SetMaxIndexId);
- return false;
- }
- if (!found)
- maxIndexId = MinimumIndexId;
-
- if (indexId <= maxIndexId) {
- INTERNAL_CONSISTENCY_ERROR(SetMaxIndexId);
- return false;
- }
-
- putInt(transaction, maxIndexIdKey, indexId);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::createIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool isUnique, bool isMultiEntry)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::createIndex");
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- if (!setMaxIndexId(levelDBTransaction, databaseId, objectStoreId, indexId))
- return false;
-
- const Vector<char> nameKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Name);
- const Vector<char> uniqueKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Unique);
- const Vector<char> keyPathKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::KeyPath);
- const Vector<char> multiEntryKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::MultiEntry);
-
- putString(levelDBTransaction, nameKey, name);
- putBool(levelDBTransaction, uniqueKey, isUnique);
- putIDBKeyPath(levelDBTransaction, keyPathKey, keyPath);
- putBool(levelDBTransaction, multiEntryKey, isMultiEntry);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::deleteIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteIndex");
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
-
- const Vector<char> indexMetaDataStart = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0);
- const Vector<char> indexMetaDataEnd = IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId, indexId);
- deleteRange(levelDBTransaction, indexMetaDataStart, indexMetaDataEnd);
-
- const Vector<char> indexDataStart = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId);
- const Vector<char> indexDataEnd = IndexDataKey::encodeMaxKey(databaseId, objectStoreId, indexId);
- deleteRange(levelDBTransaction, indexDataStart, indexDataEnd);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::putIndexDataForRecord(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, const IDBRecordIdentifier* recordIdentifier)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::putIndexDataForRecord");
- ASSERT(key.isValid());
- ASSERT(recordIdentifier);
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
-
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- const Vector<char> indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, encodeIDBKey(key), recordIdentifier->encodedPrimaryKey());
-
- Vector<char> data;
- data.appendVector(encodeVarInt(recordIdentifier->version()));
- data.appendVector(recordIdentifier->encodedPrimaryKey());
-
- levelDBTransaction->put(indexDataKey, data);
- return true;
-}
-
-static bool findGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction, const Vector<char>& target, Vector<char>& foundKey)
-{
- OwnPtr<LevelDBIterator> it = transaction->createIterator();
- it->seek(target);
-
- if (!it->isValid()) {
- it->seekToLast();
- if (!it->isValid())
- return false;
- }
-
- while (IDBBackingStoreLevelDB::compareIndexKeys(it->key(), target) > 0) {
- it->prev();
- if (!it->isValid())
- return false;
- }
-
- do {
- foundKey.clear();
- foundKey.append(it->key().begin(), it->key().end() - it->key().begin());
-
- // There can be several index keys that compare equal. We want the last one.
- it->next();
- } while (it->isValid() && !IDBBackingStoreLevelDB::compareIndexKeys(it->key(), target));
-
- return true;
-}
-
-static bool versionExists(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t version, const Vector<char>& encodedPrimaryKey, bool& exists)
-{
- const Vector<char> key = ExistsEntryKey::encode(databaseId, objectStoreId, encodedPrimaryKey);
- Vector<char> data;
-
- bool ok = transaction->safeGet(key, data, exists);
- if (!ok) {
- INTERNAL_READ_ERROR(VersionExists);
- return false;
- }
- if (!exists)
- return true;
-
- exists = (decodeInt(data.begin(), data.end()) == version);
- return true;
-}
-
-bool IDBBackingStoreLevelDB::findKeyInIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, Vector<char>& foundEncodedPrimaryKey, bool& found)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::findKeyInIndex");
- ASSERT(KeyPrefix::validIds(databaseId, objectStoreId, indexId));
-
- ASSERT(foundEncodedPrimaryKey.isEmpty());
- found = false;
-
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- const Vector<char> leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key);
- OwnPtr<LevelDBIterator> it = levelDBTransaction->createIterator();
- it->seek(leveldbKey);
-
- for (;;) {
- if (!it->isValid())
- return true;
- if (compareIndexKeys(it->key(), leveldbKey) > 0)
- return true;
-
- int64_t version;
- const char* p = decodeVarInt(it->value().begin(), it->value().end(), version);
- if (!p) {
- INTERNAL_READ_ERROR(FindKeyInIndex);
- return false;
- }
- foundEncodedPrimaryKey.append(p, it->value().end() - p);
-
- bool exists = false;
- bool ok = versionExists(levelDBTransaction, databaseId, objectStoreId, version, foundEncodedPrimaryKey, exists);
- if (!ok)
- return false;
- if (!exists) {
- // Delete stale index data entry and continue.
- levelDBTransaction->remove(it->key());
- it->next();
- continue;
- }
- found = true;
- return true;
- }
-}
-
-bool IDBBackingStoreLevelDB::getPrimaryKeyViaIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, RefPtr<IDBKey>& primaryKey)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::getPrimaryKeyViaIndex");
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
-
- bool found = false;
- Vector<char> foundEncodedPrimaryKey;
- bool ok = findKeyInIndex(transaction, databaseId, objectStoreId, indexId, key, foundEncodedPrimaryKey, found);
- if (!ok) {
- INTERNAL_READ_ERROR(GetPrimaryKeyViaIndex);
- return false;
- }
- if (found) {
- decodeIDBKey(foundEncodedPrimaryKey.begin(), foundEncodedPrimaryKey.end(), primaryKey);
- return true;
- }
-
- return true;
-}
-
-bool IDBBackingStoreLevelDB::keyExistsInIndex(IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr<IDBKey>& foundPrimaryKey, bool& exists)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInIndex");
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
-
- exists = false;
- Vector<char> foundEncodedPrimaryKey;
- bool ok = findKeyInIndex(transaction, databaseId, objectStoreId, indexId, indexKey, foundEncodedPrimaryKey, exists);
- if (!ok) {
- INTERNAL_READ_ERROR(KeyExistsInIndex);
- return false;
- }
- if (!exists)
- return true;
-
- decodeIDBKey(foundEncodedPrimaryKey.begin(), foundEncodedPrimaryKey.end(), foundPrimaryKey);
- return true;
-}
-
-
-bool IDBBackingStoreLevelDB::makeIndexWriters(int64_t transactionID, int64_t databaseID, const IDBObjectStoreMetadata& objectStore, IDBKey& primaryKey, bool keyWasGenerated, const Vector<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, Vector<RefPtr<IDBIndexWriterLevelDB>>& indexWriters, String* errorMessage, bool& completed)
-{
- ASSERT(indexIDs.size() == indexKeys.size());
- completed = false;
-
- HashMap<int64_t, IndexKeys> indexKeyMap;
- for (size_t i = 0; i < indexIDs.size(); ++i)
- indexKeyMap.add(indexIDs[i], indexKeys[i]);
-
- for (IDBObjectStoreMetadata::IndexMap::const_iterator it = objectStore.indexes.begin(); it != objectStore.indexes.end(); ++it) {
- const IDBIndexMetadata& index = it->value;
-
- IndexKeys keys = indexKeyMap.get(it->key);
- // If the objectStore is using autoIncrement, then any indexes with an identical keyPath need to also use the primary (generated) key as a key.
- if (keyWasGenerated && (index.keyPath == objectStore.keyPath))
- keys.append(&primaryKey);
-
- RefPtr<IDBIndexWriterLevelDB> indexWriter = IDBIndexWriterLevelDB::create(index, keys);
- bool canAddKeys = false;
- ASSERT(m_backingStoreTransactions.contains(transactionID));
- bool backingStoreSuccess = indexWriter->verifyIndexKeys(*this, *m_backingStoreTransactions.get(transactionID), databaseID, objectStore.id, index.id, canAddKeys, &primaryKey, errorMessage);
- if (!backingStoreSuccess)
- return false;
- if (!canAddKeys)
- return true;
-
- indexWriters.append(indexWriter.release());
- }
-
- completed = true;
- return true;
-}
-
-PassRefPtr<IDBKey> IDBBackingStoreLevelDB::generateKey(IDBTransactionBackend& transaction, int64_t databaseId, int64_t objectStoreId)
-{
- const int64_t maxGeneratorValue = 9007199254740992LL; // Maximum integer storable as ECMAScript number.
- int64_t currentNumber;
- ASSERT(m_backingStoreTransactions.contains(transaction.id()));
- bool ok = getKeyGeneratorCurrentNumber(*m_backingStoreTransactions.get(transaction.id()), databaseId, objectStoreId, currentNumber);
- if (!ok) {
- LOG_ERROR("Failed to getKeyGeneratorCurrentNumber");
- return IDBKey::createInvalid();
- }
- if (currentNumber < 0 || currentNumber > maxGeneratorValue)
- return IDBKey::createInvalid();
-
- return IDBKey::createNumber(currentNumber);
-}
-
-
-bool IDBBackingStoreLevelDB::updateKeyGenerator(IDBTransactionBackend& transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, bool checkCurrent)
-{
- ASSERT(key.type() == IDBKey::NumberType);
- ASSERT(m_backingStoreTransactions.contains(transaction.id()));
-
- return maybeUpdateKeyGeneratorCurrentNumber(*m_backingStoreTransactions.get(transaction.id()), databaseId, objectStoreId, static_cast<int64_t>(floor(key.number())) + 1, checkCurrent);
-}
-
-class ObjectStoreKeyCursorImpl : public IDBBackingStoreCursorLevelDB {
-public:
- static PassRefPtr<ObjectStoreKeyCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new ObjectStoreKeyCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new ObjectStoreKeyCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { ASSERT_NOT_REACHED(); return 0; }
- virtual bool loadCurrentRow() override;
-
-protected:
- virtual Vector<char> encodeKey(const IDBKey &key)
- {
- return ObjectStoreDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, key);
- }
-
-private:
- ObjectStoreKeyCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions)
- {
- }
-
- ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
- : IDBBackingStoreCursorLevelDB(other)
- {
- }
-};
-
-bool ObjectStoreKeyCursorImpl::loadCurrentRow()
-{
- const char* keyPosition = m_iterator->key().begin();
- const char* keyLimit = m_iterator->key().end();
-
- ObjectStoreDataKey objectStoreDataKey;
- keyPosition = ObjectStoreDataKey::decode(keyPosition, keyLimit, &objectStoreDataKey);
- if (!keyPosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- m_currentKey = objectStoreDataKey.userKey();
-
- int64_t version;
- const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version);
- if (!valuePosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- // FIXME: This re-encodes what was just decoded; try and optimize.
- m_recordIdentifier->reset(encodeIDBKey(*m_currentKey), version);
-
- return true;
-}
-
-class ObjectStoreCursorImpl : public IDBBackingStoreCursorLevelDB {
-public:
- static PassRefPtr<ObjectStoreCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new ObjectStoreCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new ObjectStoreCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { return m_currentValue; }
- virtual bool loadCurrentRow() override;
-
-protected:
- virtual Vector<char> encodeKey(const IDBKey &key)
- {
- return ObjectStoreDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, key);
- }
-
-private:
- ObjectStoreCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions)
- {
- }
-
- ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
- : IDBBackingStoreCursorLevelDB(other)
- , m_currentValue(other->m_currentValue)
- {
- }
-
- RefPtr<SharedBuffer> m_currentValue;
-};
-
-bool ObjectStoreCursorImpl::loadCurrentRow()
-{
- const char* keyPosition = m_iterator->key().begin();
- const char* keyLimit = m_iterator->key().end();
-
- ObjectStoreDataKey objectStoreDataKey;
- keyPosition = ObjectStoreDataKey::decode(keyPosition, keyLimit, &objectStoreDataKey);
- if (!keyPosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- m_currentKey = objectStoreDataKey.userKey();
-
- int64_t version;
- const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version);
- if (!valuePosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- // FIXME: This re-encodes what was just decoded; try and optimize.
- m_recordIdentifier->reset(encodeIDBKey(*m_currentKey), version);
-
- Vector<char> value;
- value.append(valuePosition, m_iterator->value().end() - valuePosition);
- m_currentValue = SharedBuffer::adoptVector(value);
- return true;
-}
-
-class IndexKeyCursorImpl final : public IDBBackingStoreCursorLevelDB {
-public:
- static PassRefPtr<IndexKeyCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new IndexKeyCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new IndexKeyCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { ASSERT_NOT_REACHED(); return 0; }
- virtual PassRefPtr<IDBKey> primaryKey() const override { return m_primaryKey; }
- virtual const IDBRecordIdentifier& recordIdentifier() const override { ASSERT_NOT_REACHED(); return *m_recordIdentifier; }
- virtual bool loadCurrentRow() override;
-
-protected:
- virtual Vector<char> encodeKey(const IDBKey &key)
- {
- return IndexDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, m_cursorOptions.indexId, key);
- }
-
-private:
- IndexKeyCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions)
- {
- }
-
- IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
- : IDBBackingStoreCursorLevelDB(other)
- , m_primaryKey(other->m_primaryKey)
- {
- }
-
- RefPtr<IDBKey> m_primaryKey;
-};
-
-bool IndexKeyCursorImpl::loadCurrentRow()
-{
- const char* keyPosition = m_iterator->key().begin();
- const char* keyLimit = m_iterator->key().end();
-
- IndexDataKey indexDataKey;
- keyPosition = IndexDataKey::decode(keyPosition, keyLimit, &indexDataKey);
-
- m_currentKey = indexDataKey.userKey();
-
- int64_t indexDataVersion;
- const char* valuePosition = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), indexDataVersion);
- if (!valuePosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- valuePosition = decodeIDBKey(valuePosition, m_iterator->value().end(), m_primaryKey);
- if (!valuePosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- Vector<char> primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey);
-
- Vector<char> result;
- bool found = false;
- bool ok = m_transaction->safeGet(primaryLevelDBKey, result, found);
- if (!ok) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
- if (!found) {
- m_transaction->remove(m_iterator->key());
- return false;
- }
-
- int64_t objectStoreDataVersion;
- const char* t = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion);
- if (!t) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- if (objectStoreDataVersion != indexDataVersion) {
- m_transaction->remove(m_iterator->key());
- return false;
- }
-
- return true;
-}
-
-class IndexCursorImpl final : public IDBBackingStoreCursorLevelDB {
-public:
- static PassRefPtr<IndexCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new IndexCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new IndexCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { return m_currentValue; }
- virtual PassRefPtr<IDBKey> primaryKey() const override { return m_primaryKey; }
- virtual const IDBRecordIdentifier& recordIdentifier() const override { ASSERT_NOT_REACHED(); return *m_recordIdentifier; }
- virtual bool loadCurrentRow() override;
-
-protected:
- virtual Vector<char> encodeKey(const IDBKey &key)
- {
- return IndexDataKey::encode(m_cursorOptions.databaseId, m_cursorOptions.objectStoreId, m_cursorOptions.indexId, key);
- }
-
-private:
- IndexCursorImpl(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- : IDBBackingStoreCursorLevelDB(cursorID, transaction, cursorOptions)
- {
- }
-
- IndexCursorImpl(const IndexCursorImpl* other)
- : IDBBackingStoreCursorLevelDB(other)
- , m_primaryKey(other->m_primaryKey)
- , m_currentValue(other->m_currentValue)
- , m_primaryLevelDBKey(other->m_primaryLevelDBKey)
- {
- }
-
- RefPtr<IDBKey> m_primaryKey;
- RefPtr<SharedBuffer> m_currentValue;
- Vector<char> m_primaryLevelDBKey;
-};
-
-bool IndexCursorImpl::loadCurrentRow()
-{
- const char* keyPosition = m_iterator->key().begin();
- const char* keyLimit = m_iterator->key().end();
-
- IndexDataKey indexDataKey;
- keyPosition = IndexDataKey::decode(keyPosition, keyLimit, &indexDataKey);
-
- m_currentKey = indexDataKey.userKey();
-
- const char* valuePosition = m_iterator->value().begin();
- const char* valueLimit = m_iterator->value().end();
-
- int64_t indexDataVersion;
- valuePosition = decodeVarInt(valuePosition, valueLimit, indexDataVersion);
- if (!valuePosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
- valuePosition = decodeIDBKey(valuePosition, valueLimit, m_primaryKey);
- if (!valuePosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- m_primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey);
-
- Vector<char> result;
- bool found = false;
- bool ok = m_transaction->safeGet(m_primaryLevelDBKey, result, found);
- if (!ok) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
- if (!found) {
- m_transaction->remove(m_iterator->key());
- return false;
- }
-
- int64_t objectStoreDataVersion;
- valuePosition = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion);
- if (!valuePosition) {
- INTERNAL_READ_ERROR(LoadCurrentRow);
- return false;
- }
-
- if (objectStoreDataVersion != indexDataVersion) {
- m_transaction->remove(m_iterator->key());
- return false;
- }
-
- Vector<char> value;
- value.append(valuePosition, result.end() - valuePosition);
- m_currentValue = SharedBuffer::adoptVector(value);
- return true;
-}
-
-static bool objectStoreCursorOptions(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction, IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
-{
- cursorOptions.databaseId = databaseId;
- cursorOptions.objectStoreId = objectStoreId;
-
- bool lowerBound = range && range->lower();
- bool upperBound = range && range->upper();
- cursorOptions.forward = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::Next);
- cursorOptions.unique = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::PrevNoDuplicate);
-
- if (!lowerBound) {
- cursorOptions.lowKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
- cursorOptions.lowOpen = true; // Not included.
- } else {
- cursorOptions.lowKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->lower());
- cursorOptions.lowOpen = range->lowerOpen();
- }
-
- if (!upperBound) {
- cursorOptions.highKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
-
- if (cursorOptions.forward)
- cursorOptions.highOpen = true; // Not included.
- else {
- // We need a key that exists.
- if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, cursorOptions.highKey))
- return false;
- cursorOptions.highOpen = false;
- }
- } else {
- cursorOptions.highKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->upper());
- cursorOptions.highOpen = range->upperOpen();
-
- if (!cursorOptions.forward) {
- // For reverse cursors, we need a key that exists.
- Vector<char> foundHighKey;
- if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, foundHighKey))
- return false;
-
- // If the target key should not be included, but we end up with a smaller key, we should include that.
- if (cursorOptions.highOpen && IDBBackingStoreLevelDB::compareIndexKeys(foundHighKey, cursorOptions.highKey) < 0)
- cursorOptions.highOpen = false;
-
- cursorOptions.highKey = foundHighKey;
- }
- }
-
- return true;
-}
-
-static bool indexCursorOptions(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction, IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
-{
- ASSERT(transaction);
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
-
- cursorOptions.databaseId = databaseId;
- cursorOptions.objectStoreId = objectStoreId;
- cursorOptions.indexId = indexId;
-
- bool lowerBound = range && range->lower();
- bool upperBound = range && range->upper();
- cursorOptions.forward = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::Next);
- cursorOptions.unique = (direction == IndexedDB::CursorDirection::NextNoDuplicate || direction == IndexedDB::CursorDirection::PrevNoDuplicate);
-
- if (!lowerBound) {
- cursorOptions.lowKey = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId);
- cursorOptions.lowOpen = false; // Included.
- } else {
- cursorOptions.lowKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->lower());
- cursorOptions.lowOpen = range->lowerOpen();
- }
-
- if (!upperBound) {
- cursorOptions.highKey = IndexDataKey::encodeMaxKey(databaseId, objectStoreId, indexId);
- cursorOptions.highOpen = false; // Included.
-
- if (!cursorOptions.forward) { // We need a key that exists.
- if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, cursorOptions.highKey))
- return false;
- cursorOptions.highOpen = false;
- }
- } else {
- cursorOptions.highKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper());
- cursorOptions.highOpen = range->upperOpen();
-
- Vector<char> foundHighKey;
- if (!findGreatestKeyLessThanOrEqual(transaction, cursorOptions.highKey, foundHighKey)) // Seek to the *last* key in the set of non-unique keys.
- return false;
-
- // If the target key should not be included, but we end up with a smaller key, we should include that.
- if (cursorOptions.highOpen && IDBBackingStoreLevelDB::compareIndexKeys(foundHighKey, cursorOptions.highKey) < 0)
- cursorOptions.highOpen = false;
-
- cursorOptions.highKey = foundHighKey;
- }
-
- return true;
-}
-PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openObjectStoreCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction)
-
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::openObjectStoreCursor");
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions;
- if (!objectStoreCursorOptions(levelDBTransaction, databaseID, objectStoreId, range, direction, cursorOptions))
- return 0;
- RefPtr<ObjectStoreCursorImpl> cursor = ObjectStoreCursorImpl::create(cursorID, levelDBTransaction, cursorOptions);
- if (!cursor->firstSeek())
- return 0;
-
- return cursor.release();
-}
-
-PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openObjectStoreKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange* range, IndexedDB::CursorDirection direction)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::openObjectStoreKeyCursor");
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions;
- if (!objectStoreCursorOptions(levelDBTransaction, databaseID, objectStoreId, range, direction, cursorOptions))
- return 0;
- RefPtr<ObjectStoreKeyCursorImpl> cursor = ObjectStoreKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions);
- if (!cursor->firstSeek())
- return 0;
-
- return cursor.release();
-}
-
-PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openIndexKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::openIndexKeyCursor");
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions;
- if (!indexCursorOptions(levelDBTransaction, databaseID, objectStoreId, indexId, range, direction, cursorOptions))
- return 0;
- RefPtr<IndexKeyCursorImpl> cursor = IndexKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions);
- if (!cursor->firstSeek())
- return 0;
-
- return cursor.release();
-}
-
-PassRefPtr<IDBBackingStoreCursorLevelDB> IDBBackingStoreLevelDB::openIndexCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::openIndexCursor");
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- IDBBackingStoreCursorLevelDB::CursorOptions cursorOptions;
- if (!indexCursorOptions(levelDBTransaction, databaseID, objectStoreId, indexId, range, direction, cursorOptions))
- return 0;
- RefPtr<IndexCursorImpl> cursor = IndexCursorImpl::create(cursorID, levelDBTransaction, cursorOptions);
- if (!cursor->firstSeek())
- return 0;
-
- return cursor.release();
-}
-
-void IDBBackingStoreLevelDB::establishBackingStoreTransaction(int64_t transactionID)
-{
- ASSERT(!m_backingStoreTransactions.contains(transactionID));
- m_backingStoreTransactions.set(transactionID, IDBBackingStoreTransactionLevelDB::create(transactionID, this));
-}
-
-void IDBBackingStoreLevelDB::removeBackingStoreTransaction(IDBBackingStoreTransactionLevelDB* transaction)
-{
- ASSERT(m_backingStoreTransactions.contains(transaction->transactionID()));
- m_backingStoreTransactions.remove(transaction->transactionID());
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h
deleted file mode 100644
index 9ef75e45c..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 IDBBackingStoreLevelDB_h
-#define IDBBackingStoreLevelDB_h
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#include "IDBBackingStoreCursorLevelDB.h"
-#include "IDBBackingStoreTransactionLevelDB.h"
-#include "IDBDatabaseMetadata.h"
-#include "IDBKey.h"
-#include "IDBRecordIdentifier.h"
-#include "IndexedDB.h"
-#include "LevelDBIterator.h"
-#include "LevelDBTransaction.h"
-#include <functional>
-#include <wtf/OwnPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/WeakPtr.h>
-
-namespace WebCore {
-
-class LevelDBComparator;
-class LevelDBDatabase;
-class LevelDBTransaction;
-class IDBIndexWriterLevelDB;
-class IDBKey;
-class IDBKeyRange;
-class IDBTransactionBackend;
-class SecurityOrigin;
-class SharedBuffer;
-
-class LevelDBFactory {
-public:
- virtual ~LevelDBFactory() { }
- virtual PassOwnPtr<LevelDBDatabase> openLevelDB(const String& fileName, const LevelDBComparator*) = 0;
- virtual bool destroyLevelDB(const String& fileName) = 0;
-};
-
-class IDBBackingStoreLevelDB : public RefCounted<IDBBackingStoreLevelDB> {
-public:
- class Transaction;
-
- virtual ~IDBBackingStoreLevelDB();
- static PassRefPtr<IDBBackingStoreLevelDB> open(const SecurityOrigin&, const String& pathBase, const String& fileIdentifier);
- static PassRefPtr<IDBBackingStoreLevelDB> open(const SecurityOrigin&, const String& pathBase, const String& fileIdentifier, LevelDBFactory*);
- static PassRefPtr<IDBBackingStoreLevelDB> openInMemory(const String& identifier);
- static PassRefPtr<IDBBackingStoreLevelDB> openInMemory(const String& identifier, LevelDBFactory*);
- WeakPtr<IDBBackingStoreLevelDB> createWeakPtr() { return m_weakFactory.createWeakPtr(); }
-
- void establishBackingStoreTransaction(int64_t transactionID);
-
- Vector<String> getDatabaseNames();
-
- // New-style asynchronous callbacks
- void getOrEstablishIDBDatabaseMetadata(const String& name, std::function<void (const IDBDatabaseMetadata&, bool success)> callbackFunction);
- void deleteDatabase(const String& name, std::function<void (bool success)> successCallback);
-
- // Old-style synchronous callbacks
- bool updateIDBDatabaseVersion(IDBBackingStoreTransactionLevelDB&, int64_t rowId, uint64_t version);
-
- bool createObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath&, bool autoIncrement);
- bool deleteObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId) WARN_UNUSED_RETURN;
-
- bool getRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, Vector<char>& record) WARN_UNUSED_RETURN;
- bool putRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, PassRefPtr<SharedBuffer> value, IDBRecordIdentifier*) WARN_UNUSED_RETURN;
- bool clearObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId) WARN_UNUSED_RETURN;
- bool deleteRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBRecordIdentifier&) WARN_UNUSED_RETURN;
- bool getKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t& currentNumber) WARN_UNUSED_RETURN;
- bool maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t newState, bool checkCurrent) WARN_UNUSED_RETURN;
- bool keyExistsInObjectStore(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, RefPtr<IDBRecordIdentifier>& foundIDBRecordIdentifier) WARN_UNUSED_RETURN;
-
- bool createIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath&, bool isUnique, bool isMultiEntry) WARN_UNUSED_RETURN;
- bool deleteIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId) WARN_UNUSED_RETURN;
- bool putIndexDataForRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const IDBRecordIdentifier*) WARN_UNUSED_RETURN;
- bool getPrimaryKeyViaIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, RefPtr<IDBKey>& primaryKey) WARN_UNUSED_RETURN;
- bool keyExistsInIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr<IDBKey>& foundPrimaryKey, bool& exists) WARN_UNUSED_RETURN;
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> openObjectStoreKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection);
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> openObjectStoreCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection);
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> openIndexKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IndexedDB::CursorDirection);
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> openIndexCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IndexedDB::CursorDirection);
-
- bool makeIndexWriters(int64_t transactionID, int64_t databaseId, const IDBObjectStoreMetadata&, IDBKey& primaryKey, bool keyWasGenerated, const Vector<int64_t>& indexIds, const Vector<Vector<RefPtr<IDBKey>>>&, Vector<RefPtr<IDBIndexWriterLevelDB>>& indexWriters, String* errorMessage, bool& completed) WARN_UNUSED_RETURN;
-
- virtual PassRefPtr<IDBKey> generateKey(IDBTransactionBackend&, int64_t databaseId, int64_t objectStoreId);
- bool updateKeyGenerator(IDBTransactionBackend&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, bool checkCurrent);
-
- LevelDBDatabase* levelDBDatabase() { return m_db.get(); }
-
- static int compareIndexKeys(const LevelDBSlice&, const LevelDBSlice&);
-
- void removeBackingStoreTransaction(IDBBackingStoreTransactionLevelDB*);
-
-private:
- IDBBackingStoreLevelDB(const String& identifier, PassOwnPtr<LevelDBDatabase>, PassOwnPtr<LevelDBComparator>);
- 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(IDBBackingStoreTransactionLevelDB&, 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;
-
- String m_identifier;
-
- OwnPtr<LevelDBDatabase> m_db;
- OwnPtr<LevelDBComparator> m_comparator;
- WeakPtrFactory<IDBBackingStoreLevelDB> m_weakFactory;
-
- HashMap<int64_t, RefPtr<IDBBackingStoreTransactionLevelDB>> m_backingStoreTransactions;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#endif // IDBBackingStoreLevelDB_h
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp
deleted file mode 100644
index a7a5f66fa..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBBackingStoreTransactionLevelDB.h"
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#include "HistogramSupport.h"
-#include "IDBBackingStoreLevelDB.h"
-#include "Logging.h"
-#include <wtf/text/CString.h>
-
-namespace WebCore {
-
-IDBBackingStoreTransactionLevelDB::IDBBackingStoreTransactionLevelDB(int64_t transactionID, IDBBackingStoreLevelDB* backingStore)
- : m_transactionID(transactionID)
- , m_backingStore(backingStore)
-{
-}
-
-IDBBackingStoreTransactionLevelDB::~IDBBackingStoreTransactionLevelDB()
-{
- if (m_backingStore)
- m_backingStore->removeBackingStoreTransaction(this);
-}
-
-void IDBBackingStoreTransactionLevelDB::begin()
-{
- LOG(StorageAPI, "IDBBackingStoreTransactionLevelDB::begin");
- ASSERT(!m_transaction);
- m_transaction = LevelDBTransaction::create(reinterpret_cast<IDBBackingStoreLevelDB*>(m_backingStore)->levelDBDatabase());
-}
-
-bool IDBBackingStoreTransactionLevelDB::commit()
-{
- LOG(StorageAPI, "IDBBackingStoreTransactionLevelDB::commit");
- ASSERT(m_transaction);
- bool result = m_transaction->commit();
- m_transaction.clear();
- if (!result)
- LOG_ERROR("IndexedDB TransactionCommit Error");
- return result;
-}
-
-void IDBBackingStoreTransactionLevelDB::rollback()
-{
- LOG(StorageAPI, "IDBBackingStoreTransactionLevelDB::rollback");
- ASSERT(m_transaction);
- m_transaction->rollback();
- m_transaction.clear();
-}
-
-void IDBBackingStoreTransactionLevelDB::resetTransaction()
-{
- ASSERT(m_backingStore);
- m_backingStore->removeBackingStoreTransaction(this);
- m_backingStore = 0;
- m_transaction = 0;
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp
deleted file mode 100644
index 3541fa88c..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2011 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 "IDBFactoryBackendLevelDB.h"
-
-#include "DOMStringList.h"
-#include "IDBBackingStoreLevelDB.h"
-#include "IDBCursorBackend.h"
-#include "IDBDatabaseBackend.h"
-#include "IDBDatabaseException.h"
-#include "IDBServerConnectionLevelDB.h"
-#include "IDBTransactionBackend.h"
-#include "IDBTransactionCoordinator.h"
-#include "Logging.h"
-#include "SecurityOrigin.h"
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-namespace WebCore {
-
-template<typename K, typename M>
-static void cleanWeakMap(HashMap<K, WeakPtr<M> >& map)
-{
- HashMap<K, WeakPtr<M> > other;
- other.swap(map);
-
- typename HashMap<K, WeakPtr<M> >::const_iterator iter = other.begin();
- while (iter != other.end()) {
- if (iter->value.get())
- map.set(iter->key, iter->value);
- ++iter;
- }
-}
-
-static String computeFileIdentifier(const SecurityOrigin& securityOrigin)
-{
- static const char levelDBFileSuffix[] = "@1";
- return securityOrigin.databaseIdentifier() + levelDBFileSuffix;
-}
-
-static String computeUniqueIdentifier(const String& name, const SecurityOrigin& securityOrigin)
-{
- return computeFileIdentifier(securityOrigin) + name;
-}
-
-IDBFactoryBackendLevelDB::IDBFactoryBackendLevelDB(const String& databaseDirectory)
- : m_databaseDirectory(databaseDirectory)
-{
-}
-
-IDBFactoryBackendLevelDB::~IDBFactoryBackendLevelDB()
-{
-}
-
-void IDBFactoryBackendLevelDB::removeIDBDatabaseBackend(const String& uniqueIdentifier)
-{
- ASSERT(m_databaseBackendMap.contains(uniqueIdentifier));
- m_databaseBackendMap.remove(uniqueIdentifier);
-}
-
-void IDBFactoryBackendLevelDB::getDatabaseNames(PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, ScriptExecutionContext*, const String& dataDirectory)
-{
- ASSERT(securityOrigin);
- LOG(StorageAPI, "IDBFactoryBackendLevelDB::getDatabaseNames");
- RefPtr<IDBBackingStoreLevelDB> backingStore = openBackingStore(*securityOrigin, dataDirectory);
- if (!backingStore) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.webkitGetDatabaseNames."));
- return;
- }
-
- RefPtr<DOMStringList> databaseNames = DOMStringList::create();
-
- Vector<String> foundNames = backingStore->getDatabaseNames();
- for (Vector<String>::const_iterator it = foundNames.begin(); it != foundNames.end(); ++it)
- databaseNames->append(*it);
-
- callbacks->onSuccess(databaseNames.release());
-}
-
-void IDBFactoryBackendLevelDB::deleteDatabase(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, ScriptExecutionContext*, const String& dataDirectory)
-{
- LOG(StorageAPI, "IDBFactoryBackendLevelDB::deleteDatabase");
- const String uniqueIdentifier = computeUniqueIdentifier(name, *securityOrigin);
-
- IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentifier);
- if (it != m_databaseBackendMap.end()) {
- // If there are any connections to the database, directly delete the
- // database.
- it->value->deleteDatabase(callbacks);
- return;
- }
-
- // FIXME: Everything from now on should be done on another thread.
- RefPtr<IDBBackingStoreLevelDB> backingStore = openBackingStore(*securityOrigin, dataDirectory);
- if (!backingStore) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.deleteDatabase."));
- return;
- }
-
- RefPtr<IDBServerConnection> serverConnection = IDBServerConnectionLevelDB::create(name, backingStore.get());
- RefPtr<IDBDatabaseBackend> databaseBackend = IDBDatabaseBackend::create(name, uniqueIdentifier, this, *serverConnection);
- if (databaseBackend) {
- m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get());
- databaseBackend->deleteDatabase(callbacks);
- m_databaseBackendMap.remove(uniqueIdentifier);
- } else
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error creating database backend for indexedDB.deleteDatabase."));
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBFactoryBackendLevelDB::openBackingStore(const SecurityOrigin& securityOrigin, const String& dataDirectory)
-{
- const String fileIdentifier = computeFileIdentifier(securityOrigin);
- const bool openInMemory = dataDirectory.isEmpty();
-
- IDBBackingStoreLevelDBMap::iterator it2 = m_backingStoreMap.find(fileIdentifier);
- if (it2 != m_backingStoreMap.end() && it2->value.get())
- return it2->value.get();
-
- RefPtr<IDBBackingStoreLevelDB> backingStore;
- if (openInMemory)
- backingStore = IDBBackingStoreLevelDB::openInMemory(fileIdentifier);
- else
- backingStore = IDBBackingStoreLevelDB::open(securityOrigin, dataDirectory, fileIdentifier);
-
- if (backingStore) {
- cleanWeakMap(m_backingStoreMap);
- m_backingStoreMap.set(fileIdentifier, backingStore->createWeakPtr());
- // If an in-memory database, bind lifetime to this factory instance.
- if (openInMemory)
- m_sessionOnlyBackingStores.add(backingStore);
-
- // All backing stores associated with this factory should be of the same type.
- ASSERT(m_sessionOnlyBackingStores.isEmpty() || openInMemory);
-
- return backingStore.release();
- }
-
- return 0;
-}
-
-void IDBFactoryBackendLevelDB::open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, const SecurityOrigin& openingOrigin, const SecurityOrigin&)
-{
- LOG(StorageAPI, "IDBFactoryBackendLevelDB::open");
- const String uniqueIdentifier = computeUniqueIdentifier(name, openingOrigin);
-
- RefPtr<IDBDatabaseBackend> databaseBackend;
- IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentifier);
- if (it == m_databaseBackendMap.end()) {
- RefPtr<IDBBackingStoreLevelDB> backingStore = openBackingStore(openingOrigin, m_databaseDirectory);
- if (!backingStore) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.open."));
- return;
- }
-
- RefPtr<IDBServerConnection> serverConnection = IDBServerConnectionLevelDB::create(name, backingStore.get());
- databaseBackend = IDBDatabaseBackend::create(name, uniqueIdentifier, this, *serverConnection);
- if (databaseBackend)
- m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get());
- else {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error creating database backend for indexeddb.open."));
- return;
- }
- } else
- databaseBackend = it->value;
-
- databaseBackend->openConnection(callbacks, databaseCallbacks, transactionId, version);
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h
deleted file mode 100644
index 004046062..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h
+++ /dev/null
@@ -1,87 +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 IDBFactoryBackendLevelDB_h
-#define IDBFactoryBackendLevelDB_h
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#include "IDBCallbacks.h"
-#include "IDBDatabaseCallbacks.h"
-#include "IDBFactoryBackendInterface.h"
-#include "SecurityOrigin.h"
-#include <wtf/HashMap.h>
-#include <wtf/HashSet.h>
-#include <wtf/RefCounted.h>
-#include <wtf/WeakPtr.h>
-#include <wtf/text/StringHash.h>
-
-namespace WebCore {
-
-class DOMStringList;
-
-class IDBBackingStoreLevelDB;
-class IDBDatabaseBackend;
-
-class IDBFactoryBackendLevelDB : public IDBFactoryBackendInterface {
-public:
- static PassRefPtr<IDBFactoryBackendLevelDB> create(const String& databaseDirectory)
- {
- return adoptRef(new IDBFactoryBackendLevelDB(databaseDirectory));
- }
- virtual ~IDBFactoryBackendLevelDB();
-
- // Notifications from weak pointers.
- virtual void removeIDBDatabaseBackend(const String& uniqueIdentifier) override final;
-
- virtual void getDatabaseNames(PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, ScriptExecutionContext*, const String& dataDir) override final;
- virtual void open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin) override final;
-
- virtual void deleteDatabase(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, ScriptExecutionContext*, const String& dataDir) override final;
-
-protected:
- virtual PassRefPtr<IDBBackingStoreLevelDB> openBackingStore(const SecurityOrigin&, const String& dataDir);
-
-private:
- explicit IDBFactoryBackendLevelDB(const String& databaseDirectory);
-
- typedef HashMap<String, RefPtr<IDBDatabaseBackend> > IDBDatabaseBackendMap;
- IDBDatabaseBackendMap m_databaseBackendMap;
-
- typedef HashMap<String, WeakPtr<IDBBackingStoreLevelDB> > IDBBackingStoreLevelDBMap;
- IDBBackingStoreLevelDBMap m_backingStoreMap;
-
- HashSet<RefPtr<IDBBackingStoreLevelDB> > m_sessionOnlyBackingStores;
-
- String m_databaseDirectory;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#endif // IDBFactoryBackendLevelDB_h
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp
deleted file mode 100644
index 75b824501..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- * 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBIndexWriterLevelDB.h"
-
-#include "IDBKey.h"
-#include <wtf/text/CString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
-
-namespace WebCore {
-
-IDBIndexWriterLevelDB::IDBIndexWriterLevelDB(const IDBIndexMetadata& metadata, const IndexKeys& keys)
- : m_indexMetadata(metadata)
- , m_indexKeys(keys)
-{
-}
-
-void IDBIndexWriterLevelDB::writeIndexKeys(const IDBRecordIdentifier* recordIdentifier, IDBBackingStoreLevelDB& backingStore, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId) const
-{
- ASSERT(recordIdentifier);
- int64_t indexId = m_indexMetadata.id;
- for (size_t i = 0; i < m_indexKeys.size(); ++i) {
- bool ok = backingStore.putIndexDataForRecord(transaction, databaseId, objectStoreId, indexId, *(m_indexKeys)[i].get(), recordIdentifier);
- // This should have already been verified as a valid write during verifyIndexKeys.
- ASSERT_UNUSED(ok, ok);
- }
-}
-
-bool IDBIndexWriterLevelDB::verifyIndexKeys(IDBBackingStoreLevelDB& backingStore, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, bool& canAddKeys, const IDBKey* primaryKey, String* errorMessage) const
-{
- canAddKeys = false;
- for (size_t i = 0; i < m_indexKeys.size(); ++i) {
- bool ok = addingKeyAllowed(backingStore, transaction, databaseId, objectStoreId, indexId, (m_indexKeys)[i].get(), primaryKey, canAddKeys);
- if (!ok)
- return false;
- if (!canAddKeys) {
- if (errorMessage)
- *errorMessage = String::format("Unable to add key to index '%s': at least one key does not satisfy the uniqueness requirements.", m_indexMetadata.name.utf8().data());
- return true;
- }
- }
- canAddKeys = true;
- return true;
-}
-
-bool IDBIndexWriterLevelDB::addingKeyAllowed(IDBBackingStoreLevelDB& backingStore, IDBBackingStoreTransactionLevelDB& transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey* indexKey, const IDBKey* primaryKey, bool& allowed) const
-{
- allowed = false;
- if (!m_indexMetadata.unique) {
- allowed = true;
- return true;
- }
-
- RefPtr<IDBKey> foundPrimaryKey;
- bool found = false;
- bool ok = backingStore.keyExistsInIndex(transaction, databaseId, objectStoreId, indexId, *indexKey, foundPrimaryKey, found);
- if (!ok)
- return false;
- if (!found || (primaryKey && foundPrimaryKey->isEqual(primaryKey)))
- allowed = true;
- return true;
-}
-
-
-} // namespace WebCore
-
-#endif // USE(LEVELDB)
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h
deleted file mode 100644
index 07e55538e..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- * 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBIndexWriterLevelDB_h
-#define IDBIndexWriterLevelDB_h
-
-#include "IDBBackingStoreLevelDB.h"
-#include "IDBDatabaseBackend.h"
-#include "IDBDatabaseMetadata.h"
-#include <wtf/RefCounted.h>
-
-#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
-
-namespace WebCore {
-
-typedef Vector<RefPtr<IDBKey>> IndexKeys;
-
-class IDBIndexWriterLevelDB : public RefCounted<IDBIndexWriterLevelDB> {
-public:
- static PassRefPtr<IDBIndexWriterLevelDB> create(const IDBIndexMetadata& indexMetadata, const IndexKeys& indexKeys)
- {
- return adoptRef(new IDBIndexWriterLevelDB(indexMetadata, indexKeys));
- }
-
- bool verifyIndexKeys(IDBBackingStoreLevelDB&, IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, bool& canAddKeys, const IDBKey* primaryKey = 0, String* errorMessage = 0) const WARN_UNUSED_RETURN;
-
- void writeIndexKeys(const IDBRecordIdentifier*, IDBBackingStoreLevelDB&, IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId) const;
-
-private:
- IDBIndexWriterLevelDB(const IDBIndexMetadata&, const IndexKeys&);
-
- bool addingKeyAllowed(IDBBackingStoreLevelDB&, IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey* indexKey, const IDBKey* primaryKey, bool& allowed) const WARN_UNUSED_RETURN;
-
- const IDBIndexMetadata m_indexMetadata;
- IndexKeys m_indexKeys;
-};
-
-} // namespace WebCore
-
-#endif // #if USE(LEVELDB)
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBIndexWriterLevelDB_h
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp
deleted file mode 100644
index a4dcb1ede..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp
+++ /dev/null
@@ -1,1807 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 "IDBLevelDBCoding.h"
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#include "IDBKey.h"
-#include "IDBKeyPath.h"
-#include "LevelDBSlice.h"
-#include <wtf/ByteOrder.h>
-#include <wtf/text/StringBuilder.h>
-
-// LevelDB stores key/value pairs. Keys and values are strings of bytes, normally of type Vector<char>.
-//
-// The keys in the backing store are variable-length tuples with different types
-// of fields. Each key in the backing store starts with a ternary prefix: (database id, object store id, index id). For each, 0 is reserved for meta-data.
-// The prefix makes sure that data for a specific database, object store, and index are grouped together. The locality is important for performance: common
-// operations should only need a minimal number of seek operations. For example, all the meta-data for a database is grouped together so that reading that
-// meta-data only requires one seek.
-//
-// Each key type has a class (in square brackets below) which knows how to encode, decode, and compare that key type.
-//
-// Global meta-data have keys with prefix (0,0,0), followed by a type byte:
-//
-// <0, 0, 0, 0> => IndexedDB/LevelDB schema version [SchemaVersionKey]
-// <0, 0, 0, 1> => The maximum database id ever allocated [MaxDatabaseIdKey]
-// <0, 0, 0, 2> => SerializedScriptValue version [DataVersionKey]
-// <0, 0, 0, 100, database id> => Existence implies the database id is in the free list [DatabaseFreeListKey]
-// <0, 0, 0, 201, utf16 origin name, utf16 database name> => Database id [DatabaseNameKey]
-//
-//
-// Database meta-data:
-//
-// Again, the prefix is followed by a type byte.
-//
-// <database id, 0, 0, 0> => utf16 origin name [DatabaseMetaDataKey]
-// <database id, 0, 0, 1> => utf16 database name [DatabaseMetaDataKey]
-// <database id, 0, 0, 2> => utf16 user version data [DatabaseMetaDataKey]
-// <database id, 0, 0, 3> => maximum object store id ever allocated [DatabaseMetaDataKey]
-// <database id, 0, 0, 4> => user integer version (var int) [DatabaseMetaDataKey]
-//
-//
-// Object store meta-data:
-//
-// The prefix is followed by a type byte, then a variable-length integer, and then another type byte.
-//
-// <database id, 0, 0, 50, object store id, 0> => utf16 object store name [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 1> => utf16 key path [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 2> => has auto increment [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 3> => is evictable [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 4> => last "version" number [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 5> => maximum index id ever allocated [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 6> => has key path (vs. null) [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 7> => key generator current number [ObjectStoreMetaDataKey]
-//
-//
-// Index meta-data:
-//
-// The prefix is followed by a type byte, then two variable-length integers, and then another type byte.
-//
-// <database id, 0, 0, 100, object store id, index id, 0> => utf16 index name [IndexMetaDataKey]
-// <database id, 0, 0, 100, object store id, index id, 1> => are index keys unique [IndexMetaDataKey]
-// <database id, 0, 0, 100, object store id, index id, 2> => utf16 key path [IndexMetaDataKey]
-// <database id, 0, 0, 100, object store id, index id, 3> => is index multi-entry [IndexMetaDataKey]
-//
-//
-// Other object store and index meta-data:
-//
-// The prefix is followed by a type byte. The object store and index id are variable length integers, the utf16 strings are variable length strings.
-//
-// <database id, 0, 0, 150, object store id> => existence implies the object store id is in the free list [ObjectStoreFreeListKey]
-// <database id, 0, 0, 151, object store id, index id> => existence implies the index id is in the free list [IndexFreeListKey]
-// <database id, 0, 0, 200, utf16 object store name> => object store id [ObjectStoreNamesKey]
-// <database id, 0, 0, 201, object store id, utf16 index name> => index id [IndexNamesKey]
-//
-//
-// Object store data:
-//
-// The prefix is followed by a type byte. The user key is an encoded IDBKey.
-//
-// <database id, object store id, 1, user key> => "version", serialized script value [ObjectStoreDataKey]
-//
-//
-// "Exists" entry:
-//
-// The prefix is followed by a type byte. The user key is an encoded IDBKey.
-//
-// <database id, object store id, 2, user key> => "version" [ExistsEntryKey]
-//
-//
-// Index data:
-//
-// The prefix is followed by a type byte. The index key is an encoded IDBKey. The sequence number is a variable length integer.
-// The primary key is an encoded IDBKey.
-//
-// <database id, object store id, index id, index key, sequence number, primary key> => "version", primary key [IndexDataKey]
-//
-// (The sequence number is obsolete; it was used to allow two entries with
-// the same user (index) key in non-unique indexes prior to the inclusion of
-// the primary key in the data. The "version" field is used to weed out stale
-// index data. Whenever new object store data is inserted, it gets a new
-// "version" number, and new index data is written with this number. When
-// the index is used for look-ups, entries are validated against the
-// "exists" entries, and records with old "version" numbers are deleted
-// when they are encountered in getPrimaryKeyViaIndex,
-// IndexCursorImpl::loadCurrentRow, and IndexKeyCursorImpl::loadCurrentRow).
-
-namespace WebCore {
-namespace IDBLevelDBCoding {
-
-#ifndef INT64_MAX
-#define INT64_MAX 0x7fffffffffffffffLL
-#endif
-#ifndef INT32_MAX
-#define INT32_MAX 0x7fffffffL
-#endif
-
-static const unsigned char IDBKeyNullTypeByte = 0;
-static const unsigned char IDBKeyStringTypeByte = 1;
-static const unsigned char IDBKeyDateTypeByte = 2;
-static const unsigned char IDBKeyNumberTypeByte = 3;
-static const unsigned char IDBKeyArrayTypeByte = 4;
-static const unsigned char IDBKeyMinKeyTypeByte = 5;
-
-static const unsigned char IDBKeyPathTypeCodedByte1 = 0;
-static const unsigned char IDBKeyPathTypeCodedByte2 = 0;
-
-static const unsigned char ObjectStoreDataIndexId = 1;
-static const unsigned char ExistsEntryIndexId = 2;
-
-static const unsigned char SchemaVersionTypeByte = 0;
-static const unsigned char MaxDatabaseIdTypeByte = 1;
-static const unsigned char DataVersionTypeByte = 2;
-static const unsigned char MaxSimpleGlobalMetaDataTypeByte = 3; // Insert before this and increment.
-static const unsigned char DatabaseFreeListTypeByte = 100;
-static const unsigned char DatabaseNameTypeByte = 201;
-
-static const unsigned char ObjectStoreMetaDataTypeByte = 50;
-static const unsigned char IndexMetaDataTypeByte = 100;
-static const unsigned char ObjectStoreFreeListTypeByte = 150;
-static const unsigned char IndexFreeListTypeByte = 151;
-static const unsigned char ObjectStoreNamesTypeByte = 200;
-static const unsigned char IndexNamesKeyTypeByte = 201;
-
-static const unsigned char ObjectMetaDataTypeMaximum = 255;
-static const unsigned char IndexMetaDataTypeMaximum = 255;
-
-Vector<char> encodeByte(unsigned char c)
-{
- Vector<char, DefaultInlineBufferSize> v;
- v.append(c);
-
- ASSERT(v.size() <= DefaultInlineBufferSize);
- return v;
-}
-
-const char* decodeByte(const char* p, const char* limit, unsigned char& foundChar)
-{
- if (p >= limit)
- return 0;
-
- foundChar = *p++;
- return p;
-}
-
-Vector<char> maxIDBKey()
-{
- return encodeByte(IDBKeyNullTypeByte);
-}
-
-Vector<char> minIDBKey()
-{
- return encodeByte(IDBKeyMinKeyTypeByte);
-}
-
-Vector<char> encodeBool(bool b)
-{
- Vector<char, DefaultInlineBufferSize> ret;
- ret.append(b ? 1 : 0);
-
- ASSERT(ret.size() <= DefaultInlineBufferSize);
- return ret;
-}
-
-bool decodeBool(const char* begin, const char* end)
-{
- ASSERT_UNUSED(end, begin < end);
- return *begin;
-}
-
-Vector<char> encodeInt(int64_t nParam)
-{
- ASSERT(nParam >= 0);
- uint64_t n = static_cast<uint64_t>(nParam);
- Vector<char, DefaultInlineBufferSize> ret;
-
- do {
- unsigned char c = n;
- ret.append(c);
- n >>= 8;
- } while (n);
-
- ASSERT(ret.size() <= DefaultInlineBufferSize);
- return ret;
-}
-
-int64_t decodeInt(const char* begin, const char* end)
-{
- ASSERT(begin <= end);
- int64_t ret = 0;
-
- int shift = 0;
- while (begin < end) {
- unsigned char c = *begin++;
- ret |= static_cast<int64_t>(c) << shift;
- shift += 8;
- }
-
- return ret;
-}
-
-static int compareInts(int64_t a, int64_t b)
-{
- ASSERT(a >= 0);
- ASSERT(b >= 0);
-
- int64_t diff = a - b;
- if (diff < 0)
- return -1;
- if (diff > 0)
- return 1;
- return 0;
-}
-
-Vector<char> encodeVarInt(int64_t nParam)
-{
- ASSERT(nParam >= 0);
- uint64_t n = static_cast<uint64_t>(nParam);
- Vector<char, DefaultInlineBufferSize> ret;
-
- do {
- unsigned char c = n & 0x7f;
- n >>= 7;
- if (n)
- c |= 0x80;
- ret.append(c);
- } while (n);
-
- ASSERT(ret.size() <= DefaultInlineBufferSize);
- return ret;
-}
-
-const char* decodeVarInt(const char* p, const char* limit, int64_t& foundInt)
-{
- ASSERT(limit >= p);
- foundInt = 0;
- int shift = 0;
-
- do {
- if (p >= limit)
- return 0;
-
- unsigned char c = *p;
- foundInt |= static_cast<int64_t>(c & 0x7f) << shift;
- shift += 7;
- } while (*p++ & 0x80);
- return p;
-}
-
-Vector<char> encodeString(const String& s)
-{
- // Backing store is UTF-16BE, convert from host endianness.
- size_t length = s.length();
- Vector<char> ret(length * sizeof(UChar));
-
- const UChar* src = s.characters();
- UChar* dst = reinterpret_cast<UChar*>(ret.data());
- for (unsigned i = 0; i < length; ++i)
- *dst++ = htons(*src++);
-
- return ret;
-}
-
-String decodeString(const char* start, const char* end)
-{
- // Backing store is UTF-16BE, convert to host endianness.
- ASSERT(end >= start);
- ASSERT(!((end - start) % sizeof(UChar)));
-
- size_t length = (end - start) / sizeof(UChar);
- Vector<UChar> buffer(length);
-
- const UChar* src = reinterpret_cast<const UChar*>(start);
- UChar* dst = buffer.data();
- for (unsigned i = 0; i < length; ++i)
- *dst++ = ntohs(*src++);
-
- return String::adopt(buffer);
-}
-
-Vector<char> encodeStringWithLength(const String& s)
-{
- Vector<char> ret = encodeVarInt(s.length());
- ret.appendVector(encodeString(s));
- return ret;
-}
-
-const char* decodeStringWithLength(const char* p, const char* limit, String& foundString)
-{
- ASSERT(limit >= p);
- int64_t len;
- p = decodeVarInt(p, limit, len);
- if (!p || len < 0 || p + len * 2 > limit)
- return 0;
-
- foundString = decodeString(p, p + len * 2);
- p += len * 2;
- return p;
-}
-
-int compareEncodedStringsWithLength(const char*& p, const char* limitP, const char*& q, const char* limitQ, bool& ok)
-{
- ASSERT(&p != &q);
- ASSERT(limitP >= p);
- ASSERT(limitQ >= q);
- int64_t lenP, lenQ;
- p = decodeVarInt(p, limitP, lenP);
- q = decodeVarInt(q, limitQ, lenQ);
- if (!p || !q || lenP < 0 || lenQ < 0) {
- ok = false;
- return 0;
- }
- ASSERT(p && q);
- ASSERT(lenP >= 0);
- ASSERT(lenQ >= 0);
- ASSERT(p + lenP * 2 <= limitP);
- ASSERT(q + lenQ * 2 <= limitQ);
-
- const char* startP = p;
- const char* startQ = q;
- p += lenP * 2;
- q += lenQ * 2;
-
- if (p > limitP || q > limitQ) {
- ok = false;
- return 0;
- }
-
- ok = true;
- const size_t lmin = static_cast<size_t>(lenP < lenQ ? lenP : lenQ);
- if (int x = memcmp(startP, startQ, lmin * 2))
- return x;
-
- if (lenP == lenQ)
- return 0;
-
- return (lenP > lenQ) ? 1 : -1;
-}
-
-Vector<char> encodeDouble(double x)
-{
- // FIXME: It would be nice if we could be byte order independent.
- const char* p = reinterpret_cast<char*>(&x);
- Vector<char, DefaultInlineBufferSize> v;
- v.append(p, sizeof(x));
-
- ASSERT(v.size() <= DefaultInlineBufferSize);
- return v;
-}
-
-const char* decodeDouble(const char* p, const char* limit, double* d)
-{
- if (p + sizeof(*d) > limit)
- return 0;
-
- char* x = reinterpret_cast<char*>(d);
- for (size_t i = 0; i < sizeof(*d); ++i)
- *x++ = *p++;
- return p;
-}
-
-Vector<char> encodeIDBKey(const IDBKey& key)
-{
- Vector<char, DefaultInlineBufferSize> ret;
- encodeIDBKey(key, ret);
- return ret;
-}
-
-void encodeIDBKey(const IDBKey& key, Vector<char, DefaultInlineBufferSize>& into)
-{
- size_t previousSize = into.size();
- ASSERT(key.isValid());
- switch (key.type()) {
- case IDBKey::InvalidType:
- case IDBKey::MinType:
- ASSERT_NOT_REACHED();
- into.appendVector(encodeByte(IDBKeyNullTypeByte));
- return;
- case IDBKey::ArrayType: {
- into.appendVector(encodeByte(IDBKeyArrayTypeByte));
- size_t length = key.array().size();
- into.appendVector(encodeVarInt(length));
- for (size_t i = 0; i < length; ++i)
- encodeIDBKey(*key.array()[i], into);
- ASSERT_UNUSED(previousSize, into.size() > previousSize);
- return;
- }
- case IDBKey::StringType:
- into.appendVector(encodeByte(IDBKeyStringTypeByte));
- into.appendVector(encodeStringWithLength(key.string()));
- ASSERT_UNUSED(previousSize, into.size() > previousSize);
- return;
- case IDBKey::DateType:
- into.appendVector(encodeByte(IDBKeyDateTypeByte));
- into.appendVector(encodeDouble(key.date()));
- ASSERT_UNUSED(previousSize, into.size() - previousSize == 9);
- return;
- case IDBKey::NumberType:
- into.appendVector(encodeByte(IDBKeyNumberTypeByte));
- into.appendVector(encodeDouble(key.number()));
- ASSERT_UNUSED(previousSize, into.size() - previousSize == 9);
- return;
- }
-
- ASSERT_NOT_REACHED();
-}
-
-
-const char* decodeIDBKey(const char* p, const char* limit, RefPtr<IDBKey>& foundKey)
-{
- ASSERT(limit >= p);
- if (p >= limit)
- return 0;
-
- unsigned char type = *p++;
-
- switch (type) {
- case IDBKeyNullTypeByte:
- foundKey = IDBKey::createInvalid();
- return p;
-
- case IDBKeyArrayTypeByte: {
- int64_t length;
- p = decodeVarInt(p, limit, length);
- if (!p || length < 0)
- return 0;
- IDBKey::KeyArray array;
- while (length--) {
- RefPtr<IDBKey> key;
- p = decodeIDBKey(p, limit, key);
- if (!p)
- return 0;
- array.append(key);
- }
- foundKey = IDBKey::createArray(array);
- return p;
- }
- case IDBKeyStringTypeByte: {
- String s;
- p = decodeStringWithLength(p, limit, s);
- if (!p)
- return 0;
- foundKey = IDBKey::createString(s);
- return p;
- }
- case IDBKeyDateTypeByte: {
- double d;
- p = decodeDouble(p, limit, &d);
- if (!p)
- return 0;
- foundKey = IDBKey::createDate(d);
- return p;
- }
- case IDBKeyNumberTypeByte: {
- double d;
- p = decodeDouble(p, limit, &d);
- if (!p)
- return 0;
- foundKey = IDBKey::createNumber(d);
- return p;
- }
- }
-
- ASSERT_NOT_REACHED();
- return 0;
-}
-
-const char* extractEncodedIDBKey(const char* start, const char* limit, Vector<char>* result = 0)
-{
- const char* p = start;
- if (p >= limit)
- return 0;
-
- unsigned char type = *p++;
-
- switch (type) {
- case IDBKeyNullTypeByte:
- case IDBKeyMinKeyTypeByte:
- break;
- case IDBKeyArrayTypeByte: {
- int64_t length;
- p = decodeVarInt(p, limit, length);
- if (!p || length < 0)
- return 0;
- while (length--) {
- p = extractEncodedIDBKey(p, limit);
- if (!p)
- return 0;
- }
- break;
- }
- case IDBKeyStringTypeByte: {
- int64_t length;
- p = decodeVarInt(p, limit, length);
- if (!p || length < 0 || p + length * 2 > limit)
- return 0;
- p += length * 2;
- break;
- }
- case IDBKeyDateTypeByte:
- case IDBKeyNumberTypeByte:
- if (p + sizeof(double) > limit)
- return 0;
- p += sizeof(double);
- break;
- }
-
- if (result) {
- ASSERT(p);
- ASSERT(p <= limit);
- result->clear();
- result->append(start, p - start);
- }
-
- return p;
-}
-
-static IDBKey::Type keyTypeByteToKeyType(unsigned char type)
-{
- switch (type) {
- case IDBKeyNullTypeByte:
- return IDBKey::InvalidType;
- case IDBKeyArrayTypeByte:
- return IDBKey::ArrayType;
- case IDBKeyStringTypeByte:
- return IDBKey::StringType;
- case IDBKeyDateTypeByte:
- return IDBKey::DateType;
- case IDBKeyNumberTypeByte:
- return IDBKey::NumberType;
- case IDBKeyMinKeyTypeByte:
- return IDBKey::MinType;
- }
-
- ASSERT_NOT_REACHED();
- return IDBKey::InvalidType;
-}
-
-static int compareEncodedIDBKeys(const char*& ptrA, const char* limitA, const char*& ptrB, const char* limitB, bool& ok)
-{
- ok = true;
- ASSERT(&ptrA != &ptrB);
- ASSERT_WITH_SECURITY_IMPLICATION(ptrA < limitA);
- ASSERT_WITH_SECURITY_IMPLICATION(ptrB < limitB);
- unsigned char typeA = *ptrA++;
- unsigned char typeB = *ptrB++;
-
- if (int x = IDBKey::compareTypes(keyTypeByteToKeyType(typeA), keyTypeByteToKeyType(typeB)))
- return x;
-
- switch (typeA) {
- case IDBKeyNullTypeByte:
- case IDBKeyMinKeyTypeByte:
- // Null type or max type; no payload to compare.
- return 0;
- case IDBKeyArrayTypeByte: {
- int64_t lengthA, lengthB;
- ptrA = decodeVarInt(ptrA, limitA, lengthA);
- ptrB = decodeVarInt(ptrB, limitB, lengthB);
- if (!ptrA || !ptrB || lengthA < 0 || lengthB < 0) {
- ok = false;
- return 0;
- }
- for (int64_t i = 0; i < lengthA && i < lengthB; ++i) {
- int result = compareEncodedIDBKeys(ptrA, limitA, ptrB, limitB, ok);
- if (!ok || result)
- return result;
- }
- if (lengthA < lengthB)
- return -1;
- if (lengthA > lengthB)
- return 1;
- return 0;
- }
- case IDBKeyStringTypeByte:
- return compareEncodedStringsWithLength(ptrA, limitA, ptrB, limitB, ok);
- case IDBKeyDateTypeByte:
- case IDBKeyNumberTypeByte: {
- double d, e;
- ptrA = decodeDouble(ptrA, limitA, &d);
- ptrB = decodeDouble(ptrB, limitB, &e);
- ASSERT(ptrA);
- ASSERT(ptrB);
- if (!ptrA || !ptrB) {
- ok = false;
- return 0;
- }
- if (d < e)
- return -1;
- if (d > e)
- return 1;
- return 0;
- }
- }
-
- ASSERT_NOT_REACHED();
- return 0;
-}
-
-int compareEncodedIDBKeys(const Vector<char>& keyA, const Vector<char>& keyB, bool& ok)
-{
- ASSERT(keyA.size() >= 1);
- ASSERT(keyB.size() >= 1);
-
- const char* ptrA = keyA.data();
- const char* limitA = ptrA + keyA.size();
- const char* ptrB = keyB.data();
- const char* limitB = ptrB + keyB.size();
-
- return compareEncodedIDBKeys(ptrA, limitA, ptrB, limitB, ok);
-}
-
-Vector<char> encodeIDBKeyPath(const IDBKeyPath& keyPath)
-{
- // May be typed, or may be a raw string. An invalid leading
- // byte is used to identify typed coding. New records are
- // always written as typed.
- Vector<char, DefaultInlineBufferSize> ret;
- ret.append(IDBKeyPathTypeCodedByte1);
- ret.append(IDBKeyPathTypeCodedByte2);
- ret.append(static_cast<char>(keyPath.type()));
- switch (keyPath.type()) {
- case IDBKeyPath::NullType:
- break;
- case IDBKeyPath::StringType:
- ret.appendVector(encodeStringWithLength(keyPath.string()));
- break;
- case IDBKeyPath::ArrayType: {
- const Vector<String>& array = keyPath.array();
- size_t count = array.size();
- ret.appendVector(encodeVarInt(count));
- for (size_t i = 0; i < count; ++i)
- ret.appendVector(encodeStringWithLength(array[i]));
- break;
- }
- }
- return ret;
-}
-
-IDBKeyPath decodeIDBKeyPath(const char* p, const char* limit)
-{
- // May be typed, or may be a raw string. An invalid leading
- // byte sequence is used to identify typed coding. New records are
- // always written as typed.
- if (p == limit || (limit - p >= 2 && (*p != IDBKeyPathTypeCodedByte1 || *(p + 1) != IDBKeyPathTypeCodedByte2)))
- return IDBKeyPath(decodeString(p, limit));
- p += 2;
-
- ASSERT(p != limit);
- IDBKeyPath::Type type = static_cast<IDBKeyPath::Type>(*p++);
- switch (type) {
- case IDBKeyPath::NullType:
- ASSERT(p == limit);
- return IDBKeyPath();
- case IDBKeyPath::StringType: {
- String string;
- p = decodeStringWithLength(p, limit, string);
- ASSERT(p == limit);
- return IDBKeyPath(string);
- }
- case IDBKeyPath::ArrayType: {
- Vector<String> array;
- int64_t count;
- p = decodeVarInt(p, limit, count);
- ASSERT(p);
- ASSERT(count >= 0);
- while (count--) {
- String string;
- p = decodeStringWithLength(p, limit, string);
- ASSERT(p);
- array.append(string);
- }
- ASSERT(p == limit);
- return IDBKeyPath(array);
- }
- }
- ASSERT_NOT_REACHED();
- return IDBKeyPath();
-}
-
-namespace {
-
-template<typename KeyType>
-int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok)
-{
- KeyType keyA;
- KeyType keyB;
-
- const char* ptrA = KeyType::decode(a.begin(), a.end(), &keyA);
- ASSERT(ptrA);
- if (!ptrA) {
- ok = false;
- return 0;
- }
- const char* ptrB = KeyType::decode(b.begin(), b.end(), &keyB);
- ASSERT(ptrB);
- if (!ptrB) {
- ok = false;
- return 0;
- }
-
- ok = true;
- return keyA.compare(keyB);
-}
-
-template<>
-int compare<ExistsEntryKey>(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok)
-{
- KeyPrefix prefixA;
- KeyPrefix prefixB;
- const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA);
- const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB);
- ASSERT(ptrA);
- ASSERT(ptrB);
- ASSERT(prefixA.m_databaseId);
- ASSERT(prefixA.m_objectStoreId);
- ASSERT(prefixA.m_indexId == ExistsEntryKey::SpecialIndexNumber);
- ASSERT(prefixB.m_databaseId);
- ASSERT(prefixB.m_objectStoreId);
- ASSERT(prefixB.m_indexId == ExistsEntryKey::SpecialIndexNumber);
- ASSERT(ptrA != a.end());
- ASSERT(ptrB != b.end());
- // Prefixes are not compared - it is assumed this was already done.
- ASSERT(!prefixA.compare(prefixB));
-
- return compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok);
-}
-
-template<>
-int compare<ObjectStoreDataKey>(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok)
-{
- KeyPrefix prefixA;
- KeyPrefix prefixB;
- const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA);
- const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB);
- ASSERT(ptrA);
- ASSERT(ptrB);
- ASSERT(prefixA.m_databaseId);
- ASSERT(prefixA.m_objectStoreId);
- ASSERT(prefixA.m_indexId == ObjectStoreDataKey::SpecialIndexNumber);
- ASSERT(prefixB.m_databaseId);
- ASSERT(prefixB.m_objectStoreId);
- ASSERT(prefixB.m_indexId == ObjectStoreDataKey::SpecialIndexNumber);
- ASSERT(ptrA != a.end());
- ASSERT(ptrB != b.end());
- // Prefixes are not compared - it is assumed this was already done.
- ASSERT(!prefixA.compare(prefixB));
-
- return compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok);
-}
-
-template<>
-int compare<IndexDataKey>(const LevelDBSlice& a, const LevelDBSlice& b, bool ignoreDuplicates, bool& ok)
-{
- KeyPrefix prefixA;
- KeyPrefix prefixB;
- const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA);
- const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB);
- ASSERT(ptrA);
- ASSERT(ptrB);
- ASSERT(prefixA.m_databaseId);
- ASSERT(prefixA.m_objectStoreId);
- ASSERT(prefixA.m_indexId >= MinimumIndexId);
- ASSERT(prefixB.m_databaseId);
- ASSERT(prefixB.m_objectStoreId);
- ASSERT(prefixB.m_indexId >= MinimumIndexId);
- ASSERT(ptrA != a.end());
- ASSERT(ptrB != b.end());
- // Prefixes are not compared - it is assumed this was already done.
- ASSERT(!prefixA.compare(prefixB));
-
- // index key
- int result = compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok);
- if (!ok || result)
- return result;
- if (ignoreDuplicates)
- return 0;
-
- // sequence number [optional]
- int64_t sequenceNumberA = -1;
- int64_t sequenceNumberB = -1;
- if (ptrA != a.end())
- ptrA = decodeVarInt(ptrA, a.end(), sequenceNumberA);
- if (ptrB != b.end())
- ptrB = decodeVarInt(ptrB, b.end(), sequenceNumberB);
-
- // primary key [optional]
- if (!ptrA || !ptrB)
- return 0;
- if (ptrA == a.end() && ptrB == b.end())
- return 0;
- if (ptrA == a.end())
- return -1;
- if (ptrB == b.end())
- return 1;
-
- result = compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end(), ok);
- if (!ok || result)
- return result;
-
- return compareInts(sequenceNumberA, sequenceNumberB);
-}
-
-int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool indexKeys, bool& ok)
-{
- const char* ptrA = a.begin();
- const char* ptrB = b.begin();
- const char* endA = a.end();
- const char* endB = b.end();
-
- KeyPrefix prefixA;
- KeyPrefix prefixB;
-
- ptrA = KeyPrefix::decode(ptrA, endA, &prefixA);
- ptrB = KeyPrefix::decode(ptrB, endB, &prefixB);
- ASSERT(ptrA);
- ASSERT(ptrB);
- if (!ptrA || !ptrB) {
- ok = false;
- return 0;
- }
-
- ok = true;
- if (int x = prefixA.compare(prefixB))
- return x;
-
- if (prefixA.type() == KeyPrefix::GlobalMetaData) {
- ASSERT(ptrA != endA);
- ASSERT(ptrB != endB);
-
- unsigned char typeByteA = *ptrA++;
- unsigned char typeByteB = *ptrB++;
-
- if (int x = typeByteA - typeByteB)
- return x;
- if (typeByteA < MaxSimpleGlobalMetaDataTypeByte)
- return 0;
-
- const bool ignoreDuplicates = false;
- if (typeByteA == DatabaseFreeListTypeByte)
- return compare<DatabaseFreeListKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == DatabaseNameTypeByte)
- return compare<DatabaseNameKey>(a, b, ignoreDuplicates, ok);
- }
-
- if (prefixA.type() == KeyPrefix::DatabaseMetaData) {
- ASSERT(ptrA != endA);
- ASSERT(ptrB != endB);
-
- unsigned char typeByteA = *ptrA++;
- unsigned char typeByteB = *ptrB++;
-
- if (int x = typeByteA - typeByteB)
- return x;
- if (typeByteA < DatabaseMetaDataKey::MaxSimpleMetaDataType)
- return 0;
-
- const bool ignoreDuplicates = false;
- if (typeByteA == ObjectStoreMetaDataTypeByte)
- return compare<ObjectStoreMetaDataKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == IndexMetaDataTypeByte)
- return compare<IndexMetaDataKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == ObjectStoreFreeListTypeByte)
- return compare<ObjectStoreFreeListKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == IndexFreeListTypeByte)
- return compare<IndexFreeListKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == ObjectStoreNamesTypeByte)
- return compare<ObjectStoreNamesKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == IndexNamesKeyTypeByte)
- return compare<IndexNamesKey>(a, b, ignoreDuplicates, ok);
- }
-
- if (prefixA.type() == KeyPrefix::ObjectStoreData) {
- if (ptrA == endA && ptrB == endB)
- return 0;
- if (ptrA == endA)
- return -1;
- if (ptrB == endB)
- return 1; // FIXME: This case of non-existing user keys should not have to be handled this way.
-
- const bool ignoreDuplicates = false;
- return compare<ObjectStoreDataKey>(a, b, ignoreDuplicates, ok);
- }
- if (prefixA.type() == KeyPrefix::ExistsEntry) {
- if (ptrA == endA && ptrB == endB)
- return 0;
- if (ptrA == endA)
- return -1;
- if (ptrB == endB)
- return 1; // FIXME: This case of non-existing user keys should not have to be handled this way.
-
- const bool ignoreDuplicates = false;
- return compare<ExistsEntryKey>(a, b, ignoreDuplicates, ok);
- }
- if (prefixA.type() == KeyPrefix::IndexData) {
- if (ptrA == endA && ptrB == endB)
- return 0;
- if (ptrA == endA)
- return -1;
- if (ptrB == endB)
- return 1; // FIXME: This case of non-existing user keys should not have to be handled this way.
-
- bool ignoreDuplicates = indexKeys;
- return compare<IndexDataKey>(a, b, ignoreDuplicates, ok);
- }
-
- ASSERT_NOT_REACHED();
- ok = false;
- return 0;
-}
-
-}
-
-int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool indexKeys)
-{
- bool ok;
- int result = compare(a, b, indexKeys, ok);
- ASSERT(ok);
- if (!ok)
- return 0;
- return result;
-}
-
-KeyPrefix::KeyPrefix()
- : m_databaseId(InvalidType)
- , m_objectStoreId(InvalidType)
- , m_indexId(InvalidType)
-{
-}
-
-KeyPrefix::KeyPrefix(int64_t databaseId)
- : m_databaseId(databaseId)
- , m_objectStoreId(0)
- , m_indexId(0)
-{
- ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
-}
-
-KeyPrefix::KeyPrefix(int64_t databaseId, int64_t objectStoreId)
- : m_databaseId(databaseId)
- , m_objectStoreId(objectStoreId)
- , m_indexId(0)
-{
- ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
- ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
-}
-
-KeyPrefix::KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
- : m_databaseId(databaseId)
- , m_objectStoreId(objectStoreId)
- , m_indexId(indexId)
-{
- ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
- ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
- ASSERT(KeyPrefix::isValidIndexId(indexId));
-}
-
-KeyPrefix::KeyPrefix(Type type, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
- : m_databaseId(databaseId)
- , m_objectStoreId(objectStoreId)
- , m_indexId(indexId)
-{
- ASSERT_UNUSED(type, type == InvalidType);
- ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
- ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
-}
-
-
-KeyPrefix KeyPrefix::createWithSpecialIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
- ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
- ASSERT(indexId);
- return KeyPrefix(InvalidType, databaseId, objectStoreId, indexId);
-}
-
-
-bool KeyPrefix::isValidDatabaseId(int64_t databaseId)
-{
- return (databaseId > 0) && (databaseId < KeyPrefix::kMaxDatabaseId);
-}
-
-bool KeyPrefix::isValidObjectStoreId(int64_t objectStoreId)
-{
- return (objectStoreId > 0) && (objectStoreId < KeyPrefix::kMaxObjectStoreId);
-}
-
-bool KeyPrefix::isValidIndexId(int64_t indexId)
-{
- return (indexId >= MinimumIndexId) && (indexId < KeyPrefix::kMaxIndexId);
-}
-
-const char* KeyPrefix::decode(const char* start, const char* limit, KeyPrefix* result)
-{
- if (start == limit)
- return 0;
-
- unsigned char firstByte = *start++;
-
- int databaseIdBytes = ((firstByte >> 5) & 0x7) + 1;
- int objectStoreIdBytes = ((firstByte >> 2) & 0x7) + 1;
- int indexIdBytes = (firstByte & 0x3) + 1;
-
- if (start + databaseIdBytes + objectStoreIdBytes + indexIdBytes > limit)
- return 0;
-
- result->m_databaseId = decodeInt(start, start + databaseIdBytes);
- start += databaseIdBytes;
- result->m_objectStoreId = decodeInt(start, start + objectStoreIdBytes);
- start += objectStoreIdBytes;
- result->m_indexId = decodeInt(start, start + indexIdBytes);
- start += indexIdBytes;
-
- return start;
-}
-
-Vector<char> KeyPrefix::encodeEmpty()
-{
- const Vector<char, 4> result(4, 0);
- ASSERT(encodeInternal(0, 0, 0) == Vector<char>(4, 0));
- return result;
-}
-
-Vector<char> KeyPrefix::encode() const
-{
- ASSERT(m_databaseId != InvalidId);
- ASSERT(m_objectStoreId != InvalidId);
- ASSERT(m_indexId != InvalidId);
- return encodeInternal(m_databaseId, m_objectStoreId, m_indexId);
-}
-
-Vector<char> KeyPrefix::encodeInternal(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- Vector<char> databaseIdString = encodeIntSafely(databaseId, kMaxDatabaseId);
- Vector<char> objectStoreIdString = encodeIntSafely(objectStoreId, kMaxObjectStoreId);
- Vector<char> indexIdString = encodeIntSafely(indexId, kMaxIndexId);
-
- ASSERT(databaseIdString.size() <= kMaxDatabaseIdSizeBytes);
- ASSERT(objectStoreIdString.size() <= kMaxObjectStoreIdSizeBytes);
- ASSERT(indexIdString.size() <= kMaxIndexIdSizeBytes);
-
- unsigned char firstByte = (databaseIdString.size() - 1) << (kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits) | (objectStoreIdString.size() - 1) << kMaxIndexIdSizeBits | (indexIdString.size() - 1);
- COMPILE_ASSERT(kMaxDatabaseIdSizeBits + kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits == sizeof(firstByte) * 8, CANT_ENCODE_IDS);
- Vector<char, DefaultInlineBufferSize> ret;
- ret.append(firstByte);
- ret.appendVector(databaseIdString);
- ret.appendVector(objectStoreIdString);
- ret.appendVector(indexIdString);
-
- ASSERT(ret.size() <= DefaultInlineBufferSize);
- return ret;
-}
-
-int KeyPrefix::compare(const KeyPrefix& other) const
-{
- ASSERT(m_databaseId != InvalidId);
- ASSERT(m_objectStoreId != InvalidId);
- ASSERT(m_indexId != InvalidId);
-
- if (m_databaseId != other.m_databaseId)
- return compareInts(m_databaseId, other.m_databaseId);
- if (m_objectStoreId != other.m_objectStoreId)
- return compareInts(m_objectStoreId, other.m_objectStoreId);
- if (m_indexId != other.m_indexId)
- return compareInts(m_indexId, other.m_indexId);
- return 0;
-}
-
-KeyPrefix::Type KeyPrefix::type() const
-{
- ASSERT(m_databaseId != InvalidId);
- ASSERT(m_objectStoreId != InvalidId);
- ASSERT(m_indexId != InvalidId);
-
- if (!m_databaseId)
- return GlobalMetaData;
- if (!m_objectStoreId)
- return DatabaseMetaData;
- if (m_indexId == ObjectStoreDataIndexId)
- return ObjectStoreData;
- if (m_indexId == ExistsEntryIndexId)
- return ExistsEntry;
- if (m_indexId >= MinimumIndexId)
- return IndexData;
-
- ASSERT_NOT_REACHED();
- return InvalidType;
-}
-
-Vector<char> SchemaVersionKey::encode()
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(SchemaVersionTypeByte));
- return ret;
-}
-
-Vector<char> MaxDatabaseIdKey::encode()
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(MaxDatabaseIdTypeByte));
- return ret;
-}
-
-Vector<char> DataVersionKey::encode()
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(DataVersionTypeByte));
- return ret;
-}
-
-DatabaseFreeListKey::DatabaseFreeListKey()
- : m_databaseId(-1)
-{
-}
-
-const char* DatabaseFreeListKey::decode(const char* start, const char* limit, DatabaseFreeListKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(!prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == DatabaseFreeListTypeByte);
- if (p == limit)
- return 0;
- return decodeVarInt(p, limit, result->m_databaseId);
-}
-
-Vector<char> DatabaseFreeListKey::encode(int64_t databaseId)
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(DatabaseFreeListTypeByte));
- ret.appendVector(encodeVarInt(databaseId));
- return ret;
-}
-
-Vector<char> DatabaseFreeListKey::encodeMaxKey()
-{
- return encode(INT64_MAX);
-}
-
-int64_t DatabaseFreeListKey::databaseId() const
-{
- ASSERT(m_databaseId >= 0);
- return m_databaseId;
-}
-
-int DatabaseFreeListKey::compare(const DatabaseFreeListKey& other) const
-{
- ASSERT(m_databaseId >= 0);
- return compareInts(m_databaseId, other.m_databaseId);
-}
-
-const char* DatabaseNameKey::decode(const char* start, const char* limit, DatabaseNameKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return p;
- ASSERT(!prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == DatabaseNameTypeByte);
- if (p == limit)
- return 0;
- p = decodeStringWithLength(p, limit, result->m_origin);
- if (!p)
- return 0;
- return decodeStringWithLength(p, limit, result->m_databaseName);
-}
-
-Vector<char> DatabaseNameKey::encode(const String& origin, const String& databaseName)
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(DatabaseNameTypeByte));
- ret.appendVector(encodeStringWithLength(origin));
- ret.appendVector(encodeStringWithLength(databaseName));
- return ret;
-}
-
-Vector<char> DatabaseNameKey::encodeMinKeyForOrigin(const String& origin)
-{
- return encode(origin, "");
-}
-
-Vector<char> DatabaseNameKey::encodeStopKeyForOrigin(const String& origin)
-{
- // just after origin in collation order
- return encodeMinKeyForOrigin(origin + "\x01");
-}
-
-int DatabaseNameKey::compare(const DatabaseNameKey& other)
-{
- if (int x = codePointCompare(m_origin, other.m_origin))
- return x;
- return codePointCompare(m_databaseName, other.m_databaseName);
-}
-
-Vector<char> DatabaseMetaDataKey::encode(int64_t databaseId, MetaDataType metaDataType)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(metaDataType));
- return ret;
-}
-
-ObjectStoreMetaDataKey::ObjectStoreMetaDataKey()
- : m_objectStoreId(-1)
- , m_metaDataType(-1)
-{
-}
-
-const char* ObjectStoreMetaDataKey::decode(const char* start, const char* limit, ObjectStoreMetaDataKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == ObjectStoreMetaDataTypeByte);
- if (p == limit)
- return 0;
- p = decodeVarInt(p, limit, result->m_objectStoreId);
- if (!p)
- return 0;
- ASSERT(result->m_objectStoreId);
- if (p == limit)
- return 0;
- return decodeByte(p, limit, result->m_metaDataType);
-}
-
-Vector<char> ObjectStoreMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, unsigned char metaDataType)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(ObjectStoreMetaDataTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- ret.appendVector(encodeByte(metaDataType));
- return ret;
-}
-
-Vector<char> ObjectStoreMetaDataKey::encodeMaxKey(int64_t databaseId)
-{
- return encode(databaseId, INT64_MAX, ObjectMetaDataTypeMaximum);
-}
-
-Vector<char> ObjectStoreMetaDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId)
-{
- return encode(databaseId, objectStoreId, ObjectMetaDataTypeMaximum);
-}
-
-int64_t ObjectStoreMetaDataKey::objectStoreId() const
-{
- ASSERT(m_objectStoreId >= 0);
- return m_objectStoreId;
-}
-unsigned char ObjectStoreMetaDataKey::metaDataType() const
-{
- return m_metaDataType;
-}
-
-int ObjectStoreMetaDataKey::compare(const ObjectStoreMetaDataKey& other)
-{
- ASSERT(m_objectStoreId >= 0);
- if (int x = compareInts(m_objectStoreId, other.m_objectStoreId))
- return x;
- int64_t result = m_metaDataType - other.m_metaDataType;
- if (result < 0)
- return -1;
- return (result > 0) ? 1 : result;
-}
-
-IndexMetaDataKey::IndexMetaDataKey()
- : m_objectStoreId(-1)
- , m_indexId(-1)
- , m_metaDataType(0)
-{
-}
-
-const char* IndexMetaDataKey::decode(const char* start, const char* limit, IndexMetaDataKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == IndexMetaDataTypeByte);
- if (p == limit)
- return 0;
- p = decodeVarInt(p, limit, result->m_objectStoreId);
- if (!p)
- return 0;
- p = decodeVarInt(p, limit, result->m_indexId);
- if (!p)
- return 0;
- if (p == limit)
- return 0;
- return decodeByte(p, limit, result->m_metaDataType);
-}
-
-Vector<char> IndexMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(IndexMetaDataTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- ret.appendVector(encodeVarInt(indexId));
- ret.appendVector(encodeByte(metaDataType));
- return ret;
-}
-
-Vector<char> IndexMetaDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId)
-{
- return encode(databaseId, objectStoreId, INT64_MAX, IndexMetaDataTypeMaximum);
-}
-
-Vector<char> IndexMetaDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- return encode(databaseId, objectStoreId, indexId, IndexMetaDataTypeMaximum);
-}
-
-int IndexMetaDataKey::compare(const IndexMetaDataKey& other)
-{
- ASSERT(m_objectStoreId >= 0);
- ASSERT(m_indexId >= 0);
-
- if (int x = compareInts(m_objectStoreId, other.m_objectStoreId))
- return x;
- if (int x = compareInts(m_indexId, other.m_indexId))
- return x;
- return m_metaDataType - other.m_metaDataType;
-}
-
-int64_t IndexMetaDataKey::indexId() const
-{
- ASSERT(m_indexId >= 0);
- return m_indexId;
-}
-
-ObjectStoreFreeListKey::ObjectStoreFreeListKey()
- : m_objectStoreId(-1)
-{
-}
-
-const char* ObjectStoreFreeListKey::decode(const char* start, const char* limit, ObjectStoreFreeListKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == ObjectStoreFreeListTypeByte);
- if (p == limit)
- return 0;
- return decodeVarInt(p, limit, result->m_objectStoreId);
-}
-
-Vector<char> ObjectStoreFreeListKey::encode(int64_t databaseId, int64_t objectStoreId)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(ObjectStoreFreeListTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- return ret;
-}
-
-Vector<char> ObjectStoreFreeListKey::encodeMaxKey(int64_t databaseId)
-{
- return encode(databaseId, INT64_MAX);
-}
-
-int64_t ObjectStoreFreeListKey::objectStoreId() const
-{
- ASSERT(m_objectStoreId >= 0);
- return m_objectStoreId;
-}
-
-int ObjectStoreFreeListKey::compare(const ObjectStoreFreeListKey& other)
-{
- // FIXME: It may seem strange that we're not comparing database id's,
- // but that comparison will have been made earlier.
- // We should probably make this more clear, though...
- ASSERT(m_objectStoreId >= 0);
- return compareInts(m_objectStoreId, other.m_objectStoreId);
-}
-
-IndexFreeListKey::IndexFreeListKey()
- : m_objectStoreId(-1)
- , m_indexId(-1)
-{
-}
-
-const char* IndexFreeListKey::decode(const char* start, const char* limit, IndexFreeListKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == IndexFreeListTypeByte);
- if (p == limit)
- return 0;
- p = decodeVarInt(p, limit, result->m_objectStoreId);
- if (!p)
- return 0;
- return decodeVarInt(p, limit, result->m_indexId);
-}
-
-Vector<char> IndexFreeListKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(IndexFreeListTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- ret.appendVector(encodeVarInt(indexId));
- return ret;
-}
-
-Vector<char> IndexFreeListKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId)
-{
- return encode(databaseId, objectStoreId, INT64_MAX);
-}
-
-int IndexFreeListKey::compare(const IndexFreeListKey& other)
-{
- ASSERT(m_objectStoreId >= 0);
- ASSERT(m_indexId >= 0);
- if (int x = compareInts(m_objectStoreId, other.m_objectStoreId))
- return x;
- return compareInts(m_indexId, other.m_indexId);
-}
-
-int64_t IndexFreeListKey::objectStoreId() const
-{
- ASSERT(m_objectStoreId >= 0);
- return m_objectStoreId;
-}
-
-int64_t IndexFreeListKey::indexId() const
-{
- ASSERT(m_indexId >= 0);
- return m_indexId;
-}
-
-// FIXME: We never use this to look up object store ids, because a mapping
-// is kept in the IDBDatabaseBackend. Can the mapping become unreliable?
-// Can we remove this?
-const char* ObjectStoreNamesKey::decode(const char* start, const char* limit, ObjectStoreNamesKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == ObjectStoreNamesTypeByte);
- return decodeStringWithLength(p, limit, result->m_objectStoreName);
-}
-
-Vector<char> ObjectStoreNamesKey::encode(int64_t databaseId, const String& objectStoreName)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(ObjectStoreNamesTypeByte));
- ret.appendVector(encodeStringWithLength(objectStoreName));
- return ret;
-}
-
-int ObjectStoreNamesKey::compare(const ObjectStoreNamesKey& other)
-{
- return codePointCompare(m_objectStoreName, other.m_objectStoreName);
-}
-
-IndexNamesKey::IndexNamesKey()
- : m_objectStoreId(-1)
-{
-}
-
-// FIXME: We never use this to look up index ids, because a mapping
-// is kept at a higher level.
-const char* IndexNamesKey::decode(const char* start, const char* limit, IndexNamesKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(!prefix.m_objectStoreId);
- ASSERT(!prefix.m_indexId);
- if (p == limit)
- return 0;
- unsigned char typeByte = 0;
- p = decodeByte(p, limit, typeByte);
- ASSERT_UNUSED(typeByte, typeByte == IndexNamesKeyTypeByte);
- if (p == limit)
- return 0;
- p = decodeVarInt(p, limit, result->m_objectStoreId);
- if (!p)
- return 0;
- return decodeStringWithLength(p, limit, result->m_indexName);
-}
-
-Vector<char> IndexNamesKey::encode(int64_t databaseId, int64_t objectStoreId, const String& indexName)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(IndexNamesKeyTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- ret.appendVector(encodeStringWithLength(indexName));
- return ret;
-}
-
-int IndexNamesKey::compare(const IndexNamesKey& other)
-{
- ASSERT(m_objectStoreId >= 0);
- if (int x = compareInts(m_objectStoreId, other.m_objectStoreId))
- return x;
- return codePointCompare(m_indexName, other.m_indexName);
-}
-
-const char* ObjectStoreDataKey::decode(const char* start, const char* end, ObjectStoreDataKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, end, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(prefix.m_objectStoreId);
- ASSERT(prefix.m_indexId == SpecialIndexNumber);
- if (p == end)
- return 0;
- return extractEncodedIDBKey(p, end, &result->m_encodedUserKey);
-}
-
-Vector<char> ObjectStoreDataKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector<char> encodedUserKey)
-{
- KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber));
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodedUserKey);
-
- return ret;
-}
-
-Vector<char> ObjectStoreDataKey::encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey)
-{
- return encode(databaseId, objectStoreId, encodeIDBKey(userKey));
-}
-
-int ObjectStoreDataKey::compare(const ObjectStoreDataKey& other, bool& ok)
-{
- return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey, ok);
-}
-
-PassRefPtr<IDBKey> ObjectStoreDataKey::userKey() const
-{
- RefPtr<IDBKey> key;
- decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key);
- return key;
-}
-
-const int64_t ObjectStoreDataKey::SpecialIndexNumber = ObjectStoreDataIndexId;
-
-const char* ExistsEntryKey::decode(const char* start, const char* end, ExistsEntryKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, end, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(prefix.m_objectStoreId);
- ASSERT(prefix.m_indexId == SpecialIndexNumber);
- if (p == end)
- return 0;
- return extractEncodedIDBKey(p, end, &result->m_encodedUserKey);
-}
-
-Vector<char> ExistsEntryKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector<char>& encodedKey)
-{
- KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber));
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodedKey);
- return ret;
-}
-
-Vector<char> ExistsEntryKey::encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey)
-{
- return encode(databaseId, objectStoreId, encodeIDBKey(userKey));
-}
-
-int ExistsEntryKey::compare(const ExistsEntryKey& other, bool& ok)
-{
- return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey, ok);
-}
-
-PassRefPtr<IDBKey> ExistsEntryKey::userKey() const
-{
- RefPtr<IDBKey> key;
- decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key);
- return key;
-}
-
-const int64_t ExistsEntryKey::SpecialIndexNumber = ExistsEntryIndexId;
-
-IndexDataKey::IndexDataKey()
- : m_databaseId(-1)
- , m_objectStoreId(-1)
- , m_indexId(-1)
- , m_sequenceNumber(-1)
-{
-}
-
-const char* IndexDataKey::decode(const char* start, const char* limit, IndexDataKey* result)
-{
- KeyPrefix prefix;
- const char* p = KeyPrefix::decode(start, limit, &prefix);
- if (!p)
- return 0;
- ASSERT(prefix.m_databaseId);
- ASSERT(prefix.m_objectStoreId);
- ASSERT(prefix.m_indexId >= MinimumIndexId);
- result->m_databaseId = prefix.m_databaseId;
- result->m_objectStoreId = prefix.m_objectStoreId;
- result->m_indexId = prefix.m_indexId;
- result->m_sequenceNumber = -1;
- result->m_encodedPrimaryKey = minIDBKey();
-
- p = extractEncodedIDBKey(p, limit, &result->m_encodedUserKey);
- if (!p)
- return 0;
-
- // [optional] sequence number
- if (p == limit)
- return p;
- p = decodeVarInt(p, limit, result->m_sequenceNumber);
- if (!p)
- return 0;
-
- // [optional] primary key
- if (p == limit)
- return p;
- p = extractEncodedIDBKey(p, limit, &result->m_encodedPrimaryKey);
- if (!p)
- return 0;
-
- return p;
-}
-
-Vector<char> IndexDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector<char>& encodedUserKey, const Vector<char>& encodedPrimaryKey, int64_t sequenceNumber)
-{
- KeyPrefix prefix(databaseId, objectStoreId, indexId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodedUserKey);
- ret.appendVector(encodeVarInt(sequenceNumber));
- ret.appendVector(encodedPrimaryKey);
- return ret;
-}
-
-Vector<char> IndexDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey)
-{
- return encode(databaseId, objectStoreId, indexId, encodeIDBKey(userKey), minIDBKey());
-}
-
-Vector<char> IndexDataKey::encodeMinKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- return encode(databaseId, objectStoreId, indexId, minIDBKey(), minIDBKey());
-}
-
-Vector<char> IndexDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- return encode(databaseId, objectStoreId, indexId, maxIDBKey(), maxIDBKey(), INT64_MAX);
-}
-
-int IndexDataKey::compare(const IndexDataKey& other, bool ignoreDuplicates, bool& ok)
-{
- ASSERT(m_databaseId >= 0);
- ASSERT(m_objectStoreId >= 0);
- ASSERT(m_indexId >= 0);
- int result = compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey, ok);
- if (!ok || result)
- return result;
- if (ignoreDuplicates)
- return 0;
- result = compareEncodedIDBKeys(m_encodedPrimaryKey, other.m_encodedPrimaryKey, ok);
- if (!ok || result)
- return result;
- return compareInts(m_sequenceNumber, other.m_sequenceNumber);
-}
-
-int64_t IndexDataKey::databaseId() const
-{
- ASSERT(m_databaseId >= 0);
- return m_databaseId;
-}
-
-int64_t IndexDataKey::objectStoreId() const
-{
- ASSERT(m_objectStoreId >= 0);
- return m_objectStoreId;
-}
-
-int64_t IndexDataKey::indexId() const
-{
- ASSERT(m_indexId >= 0);
- return m_indexId;
-}
-
-PassRefPtr<IDBKey> IndexDataKey::userKey() const
-{
- RefPtr<IDBKey> key;
- decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key);
- return key;
-}
-
-PassRefPtr<IDBKey> IndexDataKey::primaryKey() const
-{
- RefPtr<IDBKey> key;
- decodeIDBKey(m_encodedPrimaryKey.begin(), m_encodedPrimaryKey.end(), key);
- return key;
-}
-
-} // namespace IDBLevelDBCoding
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h
deleted file mode 100644
index 9ac39bfd3..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- *
- * 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 IDBLevelDBCoding_h
-#define IDBLevelDBCoding_h
-
-#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class IDBKey;
-class IDBKeyPath;
-class LevelDBSlice;
-
-namespace IDBLevelDBCoding {
-
-const unsigned char MinimumIndexId = 30;
-
-// As most of the IDBKeys and encoded values are short, we initialize some Vectors with a default inline buffer size
-// to reduce the memory re-allocations when the Vectors are appended.
-static const size_t DefaultInlineBufferSize = 32;
-
-Vector<char> encodeByte(unsigned char);
-const char* decodeByte(const char* p, const char* limit, unsigned char& foundChar);
-Vector<char> maxIDBKey();
-Vector<char> minIDBKey();
-Vector<char> encodeBool(bool);
-bool decodeBool(const char* begin, const char* end);
-Vector<char> encodeInt(int64_t);
-inline Vector<char> encodeIntSafely(int64_t nParam, int64_t max)
-{
- ASSERT_UNUSED(max, nParam <= max);
- return encodeInt(nParam);
-}
-int64_t decodeInt(const char* begin, const char* end);
-Vector<char> encodeVarInt(int64_t);
-const char* decodeVarInt(const char* p, const char* limit, int64_t& foundInt);
-Vector<char> encodeString(const String&);
-String decodeString(const char* p, const char* end);
-Vector<char> encodeStringWithLength(const String&);
-const char* decodeStringWithLength(const char* p, const char* limit, String& foundString);
-int compareEncodedStringsWithLength(const char*& p, const char* limitP, const char*& q, const char* limitQ, bool& ok);
-Vector<char> encodeDouble(double);
-const char* decodeDouble(const char* p, const char* limit, double*);
-void encodeIDBKey(const IDBKey&, Vector<char, DefaultInlineBufferSize>& into);
-Vector<char> encodeIDBKey(const IDBKey&);
-const char* decodeIDBKey(const char* p, const char* limit, RefPtr<IDBKey>& foundKey);
-const char* extractEncodedIDBKey(const char* start, const char* limit, Vector<char>* result);
-int compareEncodedIDBKeys(const Vector<char>&, const Vector<char>&, bool& ok);
-Vector<char> encodeIDBKeyPath(const IDBKeyPath&);
-IDBKeyPath decodeIDBKeyPath(const char*, const char*);
-
-int compare(const LevelDBSlice&, const LevelDBSlice&, bool indexKeys = false);
-
-class KeyPrefix {
-public:
- KeyPrefix();
- explicit KeyPrefix(int64_t databaseId);
- KeyPrefix(int64_t databaseId, int64_t objectStoreId);
- KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- static KeyPrefix createWithSpecialIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
-
- static const char* decode(const char* start, const char* limit, KeyPrefix* result);
- Vector<char> encode() const;
- static Vector<char> encodeEmpty();
- int compare(const KeyPrefix& other) const;
-
- enum Type {
- GlobalMetaData,
- DatabaseMetaData,
- ObjectStoreData,
- ExistsEntry,
- IndexData,
- InvalidType
- };
-
- static const size_t kMaxDatabaseIdSizeBits = 3;
- static const size_t kMaxObjectStoreIdSizeBits = 3;
- static const size_t kMaxIndexIdSizeBits = 2;
-
- static const size_t kMaxDatabaseIdSizeBytes = 1ULL << kMaxDatabaseIdSizeBits; // 8
- static const size_t kMaxObjectStoreIdSizeBytes = 1ULL << kMaxObjectStoreIdSizeBits; // 8
- static const size_t kMaxIndexIdSizeBytes = 1ULL << kMaxIndexIdSizeBits; // 4
-
- static const size_t kMaxDatabaseIdBits = kMaxDatabaseIdSizeBytes * 8 - 1; // 63
- static const size_t kMaxObjectStoreIdBits = kMaxObjectStoreIdSizeBytes * 8 - 1; // 63
- static const size_t kMaxIndexIdBits = kMaxIndexIdSizeBytes * 8 - 1; // 31
-
- static const int64_t kMaxDatabaseId = (1ULL << kMaxDatabaseIdBits) - 1; // max signed int64_t
- static const int64_t kMaxObjectStoreId = (1ULL << kMaxObjectStoreIdBits) - 1; // max signed int64_t
- static const int64_t kMaxIndexId = (1ULL << kMaxIndexIdBits) - 1; // max signed int32_t
-
- static bool isValidDatabaseId(int64_t databaseId);
- static bool isValidObjectStoreId(int64_t indexId);
- static bool isValidIndexId(int64_t indexId);
- static bool validIds(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
- {
- return isValidDatabaseId(databaseId) && isValidObjectStoreId(objectStoreId) && isValidIndexId(indexId);
- }
- static bool validIds(int64_t databaseId, int64_t objectStoreId)
- {
- return isValidDatabaseId(databaseId) && isValidObjectStoreId(objectStoreId);
- }
-
- Type type() const;
-
- int64_t m_databaseId;
- int64_t m_objectStoreId;
- int64_t m_indexId;
-
- static const int64_t InvalidId = -1;
-
-private:
- static Vector<char> encodeInternal(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- // Special constructor for createWithSpecialIndex()
- KeyPrefix(Type, int64_t databaseId, int64_t objectStoreId, int64_t indexId);
-};
-
-class SchemaVersionKey {
-public:
- static Vector<char> encode();
-};
-
-class MaxDatabaseIdKey {
-public:
- static Vector<char> encode();
-};
-
-class DataVersionKey {
-public:
- static Vector<char> encode();
-};
-
-class DatabaseFreeListKey {
-public:
- DatabaseFreeListKey();
- static const char* decode(const char* start, const char* limit, DatabaseFreeListKey* result);
- static Vector<char> encode(int64_t databaseId);
- static Vector<char> encodeMaxKey();
- int64_t databaseId() const;
- int compare(const DatabaseFreeListKey& other) const;
-
-private:
- int64_t m_databaseId;
-};
-
-class DatabaseNameKey {
-public:
- static const char* decode(const char* start, const char* limit, DatabaseNameKey* result);
- static Vector<char> encode(const String& origin, const String& databaseName);
- static Vector<char> encodeMinKeyForOrigin(const String& origin);
- static Vector<char> encodeStopKeyForOrigin(const String& origin);
- String origin() const { return m_origin; }
- String databaseName() const { return m_databaseName; }
- int compare(const DatabaseNameKey& other);
-
-private:
- String m_origin; // FIXME: Store encoded strings, or just pointers.
- String m_databaseName;
-};
-
-class DatabaseMetaDataKey {
-public:
- enum MetaDataType {
- OriginName = 0,
- DatabaseName = 1,
- UserVersion = 2,
- MaxObjectStoreId = 3,
- UserIntVersion = 4,
- MaxSimpleMetaDataType = 5
- };
-
- static Vector<char> encode(int64_t databaseId, MetaDataType);
-};
-
-class ObjectStoreMetaDataKey {
-public:
- enum MetaDataType {
- Name = 0,
- KeyPath = 1,
- AutoIncrement = 2,
- Evictable = 3,
- LastVersion = 4,
- MaxIndexId = 5,
- HasKeyPath = 6,
- KeyGeneratorCurrentNumber = 7
- };
-
- ObjectStoreMetaDataKey();
- static const char* decode(const char* start, const char* limit, ObjectStoreMetaDataKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, unsigned char metaDataType);
- static Vector<char> encodeMaxKey(int64_t databaseId);
- static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId);
- int64_t objectStoreId() const;
- unsigned char metaDataType() const;
- int compare(const ObjectStoreMetaDataKey& other);
-
-private:
- int64_t m_objectStoreId;
- unsigned char m_metaDataType;
-};
-
-class IndexMetaDataKey {
-public:
- enum MetaDataType {
- Name = 0,
- Unique = 1,
- KeyPath = 2,
- MultiEntry = 3
- };
-
- IndexMetaDataKey();
- static const char* decode(const char* start, const char* limit, IndexMetaDataKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType);
- static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId);
- static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- int compare(const IndexMetaDataKey& other);
- int64_t indexId() const;
- unsigned char metaDataType() const { return m_metaDataType; }
-
-private:
- int64_t m_objectStoreId;
- int64_t m_indexId;
- unsigned char m_metaDataType;
-};
-
-class ObjectStoreFreeListKey {
-public:
- ObjectStoreFreeListKey();
- static const char* decode(const char* start, const char* limit, ObjectStoreFreeListKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId);
- static Vector<char> encodeMaxKey(int64_t databaseId);
- int64_t objectStoreId() const;
- int compare(const ObjectStoreFreeListKey& other);
-
-private:
- int64_t m_objectStoreId;
-};
-
-class IndexFreeListKey {
-public:
- IndexFreeListKey();
- static const char* decode(const char* start, const char* limit, IndexFreeListKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId);
- int compare(const IndexFreeListKey& other);
- int64_t objectStoreId() const;
- int64_t indexId() const;
-
-private:
- int64_t m_objectStoreId;
- int64_t m_indexId;
-};
-
-class ObjectStoreNamesKey {
-public:
- // FIXME: We never use this to look up object store ids, because a mapping
- // is kept in the IDBDatabaseBackend. Can the mapping become unreliable?
- // Can we remove this?
- static const char* decode(const char* start, const char* limit, ObjectStoreNamesKey* result);
- static Vector<char> encode(int64_t databaseId, const String& objectStoreName);
- int compare(const ObjectStoreNamesKey& other);
- String objectStoreName() const { return m_objectStoreName; }
-
-private:
- String m_objectStoreName; // FIXME: Store the encoded string, or just pointers to it.
-};
-
-class IndexNamesKey {
-public:
- IndexNamesKey();
- // FIXME: We never use this to look up index ids, because a mapping
- // is kept at a higher level.
- static const char* decode(const char* start, const char* limit, IndexNamesKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const String& indexName);
- int compare(const IndexNamesKey& other);
- String indexName() const { return m_indexName; }
-
-private:
- int64_t m_objectStoreId;
- String m_indexName;
-};
-
-class ObjectStoreDataKey {
-public:
- static const char* decode(const char* start, const char* end, ObjectStoreDataKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char> encodedUserKey);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey);
- int compare(const ObjectStoreDataKey& other, bool& ok);
- PassRefPtr<IDBKey> userKey() const;
- static const int64_t SpecialIndexNumber;
-
-private:
- Vector<char> m_encodedUserKey;
-};
-
-class ExistsEntryKey {
-public:
- static const char* decode(const char* start, const char* end, ExistsEntryKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char>& encodedKey);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey);
- int compare(const ExistsEntryKey& other, bool& ok);
- PassRefPtr<IDBKey> userKey() const;
-
- static const int64_t SpecialIndexNumber;
-
-private:
- Vector<char> m_encodedUserKey;
-};
-
-class IndexDataKey {
-public:
- IndexDataKey();
- static const char* decode(const char* start, const char* limit, IndexDataKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector<char>& encodedUserKey, const Vector<char>& encodedPrimaryKey, int64_t sequenceNumber = 0);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey);
- static Vector<char> encodeMinKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- int compare(const IndexDataKey& other, bool ignoreDuplicates, bool& ok);
- int64_t databaseId() const;
- int64_t objectStoreId() const;
- int64_t indexId() const;
- PassRefPtr<IDBKey> userKey() const;
- PassRefPtr<IDBKey> primaryKey() const;
-
-private:
- int64_t m_databaseId;
- int64_t m_objectStoreId;
- int64_t m_indexId;
- Vector<char> m_encodedUserKey;
- Vector<char> m_encodedPrimaryKey;
- int64_t m_sequenceNumber;
-};
-
-} // namespace IDBLevelDBCoding
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
-
-#endif // IDBLevelDBCoding_h
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp b/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp
deleted file mode 100644
index b7333f965..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBServerConnectionLevelDB.h"
-
-#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
-
-#include "IDBBackingStoreCursorLevelDB.h"
-#include "IDBBackingStoreLevelDB.h"
-#include "IDBBackingStoreTransactionLevelDB.h"
-#include "IDBCursorBackend.h"
-#include "IDBFactoryBackendLevelDB.h"
-#include "IDBIndexWriterLevelDB.h"
-#include <wtf/MainThread.h>
-
-#define ASYNC_COMPLETION_CALLBACK_WITH_ARG(callback, arg) \
- callOnMainThread([callback, arg]() { \
- callback(arg); \
- });
-
-#define ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(callback, arg1, arg2) \
- callOnMainThread([callback]() { \
- callback(arg1, arg2); \
- });
-
-#define ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(callback) \
- callOnMainThread([callback]() { \
- callback(0); \
- });
-
-#define EMPTY_ASYNC_COMPLETION_CALLBACK(callback) \
- callOnMainThread([callback]() { \
- callback(); \
- });
-
-
-namespace WebCore {
-
-IDBServerConnectionLevelDB::IDBServerConnectionLevelDB(const String& databaseName, IDBBackingStoreLevelDB* backingStore)
- : m_backingStore(backingStore)
- , m_nextCursorID(1)
- , m_closed(false)
- , m_databaseName(databaseName)
-{
-}
-
-IDBServerConnectionLevelDB::~IDBServerConnectionLevelDB()
-{
-}
-
-bool IDBServerConnectionLevelDB::isClosed()
-{
- return m_closed;
-}
-
-void IDBServerConnectionLevelDB::getOrEstablishIDBDatabaseMetadata(GetIDBDatabaseMetadataFunction callback)
-{
- RefPtr<IDBServerConnection> self(this);
- m_backingStore->getOrEstablishIDBDatabaseMetadata(m_databaseName, [self, this, callback](const IDBDatabaseMetadata& metadata, bool success) {
- callback(metadata, success);
- });
-}
-
-void IDBServerConnectionLevelDB::deleteDatabase(const String& name, BoolCallbackFunction successCallback)
-{
- RefPtr<IDBServerConnection> self(this);
- m_backingStore->deleteDatabase(name, [self, this, successCallback](bool success) {
- successCallback(success);
- });
-}
-
-void IDBServerConnectionLevelDB::close()
-{
- m_backingStore.clear();
- m_closed = true;
-}
-
-void IDBServerConnectionLevelDB::openTransaction(int64_t transactionID, const HashSet<int64_t>&, IndexedDB::TransactionMode, BoolCallbackFunction successCallback)
-{
- if (!m_backingStore) {
- callOnMainThread([successCallback]() {
- successCallback(false);
- });
- return;
- }
-
- m_backingStore->establishBackingStoreTransaction(transactionID);
- callOnMainThread([successCallback]() {
- successCallback(true);
- });
-}
-
-void IDBServerConnectionLevelDB::beginTransaction(int64_t transactionID, std::function<void()> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> transaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(transaction);
-
- transaction->begin();
- callOnMainThread(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> transaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(transaction);
-
- bool result = transaction->commit();
- callOnMainThread([successCallback, result]() {
- successCallback(result);
- });
-}
-
-void IDBServerConnectionLevelDB::resetTransaction(int64_t transactionID, std::function<void()> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> transaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(transaction);
-
- transaction->resetTransaction();
- callOnMainThread(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::rollbackTransaction(int64_t transactionID, std::function<void()> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> transaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(transaction);
-
- transaction->rollback();
- callOnMainThread(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata& objectStoreMetadata, IDBKey& primaryKey, const Vector<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> backingStoreTransaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(backingStoreTransaction);
-
- RefPtr<IDBRecordIdentifier> recordIdentifier;
- bool ok = m_backingStore->keyExistsInObjectStore(*backingStoreTransaction, databaseID, objectStoreID, primaryKey, recordIdentifier);
- if (!ok) {
- callOnMainThread([completionCallback]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: setting index keys."));
- });
- return;
- }
- if (!recordIdentifier) {
- callOnMainThread([completionCallback]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: setting index keys for object store."));
- });
- return;
- }
-
- Vector<RefPtr<IDBIndexWriterLevelDB>> indexWriters;
- String errorMessage;
- bool obeysConstraints = false;
-
- bool backingStoreSuccess = m_backingStore->makeIndexWriters(transactionID, databaseID, objectStoreMetadata, primaryKey, false, indexIDs, indexKeys, indexWriters, &errorMessage, obeysConstraints);
- if (!backingStoreSuccess) {
- callOnMainThread([completionCallback]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error updating index keys."));
- });
- return;
- }
- if (!obeysConstraints) {
- callOnMainThread([completionCallback, errorMessage]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::ConstraintError, errorMessage));
- });
- return;
- }
-
- for (size_t i = 0; i < indexWriters.size(); ++i) {
- IDBIndexWriterLevelDB* indexWriter = indexWriters[i].get();
- indexWriter->writeIndexKeys(recordIdentifier.get(), *m_backingStore, *backingStoreTransaction, databaseID, objectStoreID);
- }
-
- ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::createObjectStore(IDBTransactionBackend& transaction, const CreateObjectStoreOperation& operation, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- String objectStoreName = operation.objectStoreMetadata().name;
-
- if (!m_backingStore->createObjectStore(*backingStoreTransaction, transaction.database().id(), operation.objectStoreMetadata().id, objectStoreName, operation.objectStoreMetadata().keyPath, operation.objectStoreMetadata().autoIncrement)) {
- callOnMainThread([completionCallback, objectStoreName]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error creating object store '%s'.", objectStoreName.utf8().data())));
- });
- return;
- }
- ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::createIndex(IDBTransactionBackend& transaction, const CreateIndexOperation& operation, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- const IDBIndexMetadata& indexMetadata = operation.idbIndexMetadata();
- if (!m_backingStore->createIndex(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), indexMetadata.id, indexMetadata.name, indexMetadata.keyPath, indexMetadata.unique, indexMetadata.multiEntry)) {
- callOnMainThread([completionCallback, indexMetadata]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error when trying to create index '%s'.", indexMetadata.name.utf8().data())));
- });
- return;
- }
- ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::deleteIndex(IDBTransactionBackend& transaction, const DeleteIndexOperation& operation, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- const IDBIndexMetadata& indexMetadata = operation.idbIndexMetadata();
- if (!m_backingStore->deleteIndex(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), indexMetadata.id)) {
- callOnMainThread([completionCallback, indexMetadata]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error deleting index '%s'.", indexMetadata.name.utf8().data())));
- });
- return;
- }
- ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::get(IDBTransactionBackend& transaction, const GetOperation& operation, std::function<void(const IDBGetResult&, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- RefPtr<IDBKey> key;
-
- if (operation.keyRange()->isOnlyKey())
- key = operation.keyRange()->lower();
- else {
- RefPtr<IDBBackingStoreCursorLevelDB> backingStoreCursor;
- int64_t cursorID = m_nextCursorID++;
-
- if (operation.indexID() == IDBIndexMetadata::InvalidId) {
- ASSERT(operation.cursorType() != IndexedDB::CursorType::KeyOnly);
- // ObjectStore Retrieval Operation
- backingStoreCursor = m_backingStore->openObjectStoreCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), IndexedDB::CursorDirection::Next);
- } else {
- if (operation.cursorType() == IndexedDB::CursorType::KeyOnly) {
- // Index Value Retrieval Operation
- backingStoreCursor = m_backingStore->openIndexKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), IndexedDB::CursorDirection::Next);
- } else {
- // Index Referenced Value Retrieval Operation
- backingStoreCursor = m_backingStore->openIndexCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), IndexedDB::CursorDirection::Next);
- }
- }
-
- if (!backingStoreCursor) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr);
- return;
- }
-
- key = backingStoreCursor->key();
- }
-
- RefPtr<IDBKey> primaryKey;
- bool ok;
- if (operation.indexID() == IDBIndexMetadata::InvalidId) {
- // Object Store Retrieval Operation
- Vector<char> value;
- ok = m_backingStore->getRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), *key, value);
- if (!ok) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error in getRecord."));
- return;
- }
-
- if (value.isEmpty()) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr);
- return;
- }
-
- IDBGetResult result;
-
- if (operation.autoIncrement() && !operation.keyPath().isNull())
- result = IDBGetResult(SharedBuffer::adoptVector(value), key, operation.keyPath());
- else
- result = IDBGetResult(SharedBuffer::adoptVector(value));
-
- callOnMainThread([completionCallback, result]() {
- completionCallback(result, nullptr);
- });
- return;
- }
-
- // From here we are dealing only with indexes.
- ok = m_backingStore->getPrimaryKeyViaIndex(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), *key, primaryKey);
- if (!ok) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error in getPrimaryKeyViaIndex."));
- return;
- }
- if (!primaryKey) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr);
- return;
- }
- if (operation.cursorType() == IndexedDB::CursorType::KeyOnly) {
- // Index Value Retrieval Operation
- IDBGetResult result(primaryKey.release());
- callOnMainThread([completionCallback, result]() {
- completionCallback(result, nullptr);
- });
- return;
- }
-
- // Index Referenced Value Retrieval Operation
- Vector<char> value;
- ok = m_backingStore->getRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), *primaryKey, value);
- if (!ok) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error in getRecord."));
- return;
- }
-
- if (value.isEmpty()) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, IDBGetResult(), nullptr);
- return;
- }
- if (operation.autoIncrement() && !operation.keyPath().isNull()) {
- IDBGetResult result(SharedBuffer::adoptVector(value), key, operation.keyPath());
- callOnMainThread([completionCallback, result]() {
- completionCallback(result, nullptr);
- });
-
- return;
- }
-
- IDBGetResult result(SharedBuffer::adoptVector(value));
- callOnMainThread([completionCallback, result]() {
- completionCallback(result, nullptr);
- });
-}
-
-void IDBServerConnectionLevelDB::put(IDBTransactionBackend& transaction, const PutOperation& operation, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- bool keyWasGenerated = false;
-
- RefPtr<IDBKey> key;
- if (operation.putMode() != IDBDatabaseBackend::CursorUpdate && operation.objectStore().autoIncrement && !operation.key()) {
- RefPtr<IDBKey> autoIncKey = m_backingStore->generateKey(transaction, transaction.database().id(), operation.objectStore().id);
- keyWasGenerated = true;
- if (!autoIncKey->isValid()) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::ConstraintError, "Maximum key generator value reached."));
- return;
- }
- key = autoIncKey;
- } else
- key = operation.key();
-
- ASSERT(key);
- ASSERT(key->isValid());
-
- RefPtr<IDBRecordIdentifier> recordIdentifier;
- if (operation.putMode() == IDBDatabaseBackend::AddOnly) {
- bool ok = m_backingStore->keyExistsInObjectStore(*backingStoreTransaction, transaction.database().id(), operation.objectStore().id, *key, recordIdentifier);
- if (!ok) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error checking key existence."));
- return;
- }
- if (recordIdentifier) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::ConstraintError, "Key already exists in the object store."));
- return;
- }
- }
-
- Vector<RefPtr<IDBIndexWriterLevelDB>> indexWriters;
- String errorMessage;
- bool obeysConstraints = false;
- bool backingStoreSuccess = m_backingStore->makeIndexWriters(transaction.id(), transaction.database().id(), operation.objectStore(), *key, keyWasGenerated, operation.indexIDs(), operation.indexKeys(), indexWriters, &errorMessage, obeysConstraints);
- if (!backingStoreSuccess) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error updating index keys."));
- return;
- }
- if (!obeysConstraints) {
- callOnMainThread([completionCallback, errorMessage]() { \
- completionCallback(nullptr, IDBDatabaseError::create(IDBDatabaseException::ConstraintError, errorMessage)); \
- });
- return;
- }
-
- // Before this point, don't do any mutation. After this point, rollback the transaction in case of error.
- backingStoreSuccess = m_backingStore->putRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStore().id, *key, operation.value(), recordIdentifier.get());
- if (!backingStoreSuccess) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error performing put/add."));
- return;
- }
-
- for (size_t i = 0; i < indexWriters.size(); ++i) {
- IDBIndexWriterLevelDB* indexWriter = indexWriters[i].get();
- indexWriter->writeIndexKeys(recordIdentifier.get(), *m_backingStore, *backingStoreTransaction, transaction.database().id(), operation.objectStore().id);
- }
-
- if (operation.objectStore().autoIncrement && operation.putMode() != IDBDatabaseBackend::CursorUpdate && key->type() == IDBKey::NumberType) {
- bool ok = m_backingStore->updateKeyGenerator(transaction, transaction.database().id(), operation.objectStore().id, *key, !keyWasGenerated);
- if (!ok) {
- ASYNC_COMPLETION_CALLBACK_WITH_TWO_ARGS(completionCallback, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error updating key generator."));
- return;
- }
- }
-
- callOnMainThread([completionCallback, key]() { \
- completionCallback(key.get(), nullptr); \
- });
-}
-
-void IDBServerConnectionLevelDB::openCursor(IDBTransactionBackend& transaction, const OpenCursorOperation& operation, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- // The frontend has begun indexing, so this pauses the transaction
- // until the indexing is complete. This can't happen any earlier
- // because we don't want to switch to early mode in case multiple
- // indexes are being created in a row, with put()'s in between.
- if (operation.taskType() == IDBDatabaseBackend::PreemptiveTask)
- transaction.addPreemptiveEvent();
-
- int64_t cursorID = m_nextCursorID++;
-
- RefPtr<IDBBackingStoreCursorLevelDB> backingStoreCursor;
- if (operation.indexID() == IDBIndexMetadata::InvalidId) {
- ASSERT(operation.cursorType() != IndexedDB::CursorType::KeyOnly);
- backingStoreCursor = m_backingStore->openObjectStoreCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), operation.direction());
- } else {
- ASSERT(operation.taskType() == IDBDatabaseBackend::NormalTask);
- if (operation.cursorType() == IndexedDB::CursorType::KeyOnly)
- backingStoreCursor = m_backingStore->openIndexKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), operation.direction());
- else
- backingStoreCursor = m_backingStore->openIndexCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), operation.direction());
- }
-
- if (!backingStoreCursor) {
- // FIXME: Should it be an error to not have a backing store cursor?
- cursorID = 0;
- }
-
- callOnMainThread([completionCallback, cursorID]() {
- completionCallback(cursorID, nullptr);
- });
-}
-
-void IDBServerConnectionLevelDB::count(IDBTransactionBackend& transaction, const CountOperation& operation, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- uint32_t count = 0;
- RefPtr<IDBBackingStoreCursorLevelDB> backingStoreCursor;
-
- int64_t cursorID = m_nextCursorID++;
-
- if (operation.indexID() == IDBIndexMetadata::InvalidId)
- backingStoreCursor = m_backingStore->openObjectStoreKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), IndexedDB::CursorDirection::Next);
- else
- backingStoreCursor = m_backingStore->openIndexKeyCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.indexID(), operation.keyRange(), IndexedDB::CursorDirection::Next);
- if (!backingStoreCursor) {
- // FIXME: Is this an error case?
- callOnMainThread([completionCallback, count]() {
- completionCallback(count, nullptr);
- });
- return;
- }
-
- do {
- ++count;
- } while (backingStoreCursor->continueFunction(0));
-
- callOnMainThread([completionCallback, count]() {
- completionCallback(count, nullptr);
- });
-}
-
-void IDBServerConnectionLevelDB::deleteRange(IDBTransactionBackend& transaction, const DeleteRangeOperation& operation, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- int64_t cursorID = m_nextCursorID++;
-
- RefPtr<IDBBackingStoreCursorLevelDB> backingStoreCursor = m_backingStore->openObjectStoreCursor(cursorID, *backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), operation.keyRange(), IndexedDB::CursorDirection::Next);
- if (backingStoreCursor) {
- do {
- if (!m_backingStore->deleteRecord(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID(), backingStoreCursor->recordIdentifier())) {
- callOnMainThread([completionCallback]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error deleting data in range"));
- });
- return;
- }
- } while (backingStoreCursor->continueFunction(0));
- }
-
- callOnMainThread([completionCallback]() {
- completionCallback(nullptr);
- });
-}
-
-void IDBServerConnectionLevelDB::clearObjectStore(IDBTransactionBackend& transaction, const ClearObjectStoreOperation& operation, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- if (!m_backingStore->clearObjectStore(*backingStoreTransaction, transaction.database().id(), operation.objectStoreID())) {
- callOnMainThread([completionCallback]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error clearing object store"));
- });
- return;
- }
-
- ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::deleteObjectStore(IDBTransactionBackend& transaction, const DeleteObjectStoreOperation& operation, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- const IDBObjectStoreMetadata& objectStoreMetadata = operation.objectStoreMetadata();
-
- if (!m_backingStore->deleteObjectStore(*backingStoreTransaction, transaction.database().id(), objectStoreMetadata.id)) {
- callOnMainThread([completionCallback, objectStoreMetadata]() {
- completionCallback(IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error deleting object store '%s'.", objectStoreMetadata.name.utf8().data())));
- });
- return;
- }
- ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::changeDatabaseVersion(IDBTransactionBackend& transaction, const IDBDatabaseBackend::VersionChangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- IDBDatabaseBackend& database = transaction.database();
- int64_t databaseId = database.id();
-
- if (!m_backingStore->updateIDBDatabaseVersion(*backingStoreTransaction, databaseId, database.metadata().version)) {
- RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error writing data to stable storage when updating version.");
- ASYNC_COMPLETION_CALLBACK_WITH_ARG(completionCallback, error);
- return;
- }
-
- ASYNC_COMPLETION_CALLBACK_WITH_NULL_ARG(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::cursorAdvance(IDBCursorBackend& cursor, const CursorAdvanceOperation& operation, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreCursorLevelDB* backingStoreCursor = cursor.id() ? m_backingStoreCursors.get(cursor.id()) : 0;
-#ifndef NDEBUG
- if (cursor.id())
- ASSERT(backingStoreCursor);
-#endif
-
- if (!backingStoreCursor || !backingStoreCursor->advance(operation.count())) {
- m_backingStoreCursors.remove(cursor.id());
-
- callOnMainThread([completionCallback]() {
- completionCallback(nullptr, nullptr, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor"));
- });
-
- return;
- }
-
- RefPtr<IDBKey> key = backingStoreCursor->key(), primaryKey = backingStoreCursor->primaryKey();
- RefPtr<SharedBuffer> value = backingStoreCursor->value();
-
- callOnMainThread([completionCallback, key, primaryKey, value]() {
- completionCallback(key, primaryKey, value, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor"));
- });
-}
-
-void IDBServerConnectionLevelDB::cursorIterate(IDBCursorBackend& cursor, const CursorIterationOperation& operation, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreCursorLevelDB* backingStoreCursor = cursor.id() ? m_backingStoreCursors.get(cursor.id()) : 0;
-#ifndef NDEBUG
- if (cursor.id())
- ASSERT(backingStoreCursor);
-#endif
-
- if (!backingStoreCursor || !backingStoreCursor->continueFunction(operation.key())) {
- m_backingStoreCursors.remove(cursor.id());
-
- callOnMainThread([completionCallback]() {
- completionCallback(nullptr, nullptr, nullptr, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor"));
- });
-
- return;
- }
-
- RefPtr<IDBKey> key = backingStoreCursor->key(), primaryKey = backingStoreCursor->primaryKey();
- RefPtr<SharedBuffer> value = backingStoreCursor->value();
-
- callOnMainThread([completionCallback, key, primaryKey, value]() {
- completionCallback(key, primaryKey, value, IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Unknown error advancing cursor"));
- });
-}
-
-} // namespace WebCore
-
-#endif // USE(LEVELDB)
-#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h b/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h
deleted file mode 100644
index e574f389f..000000000
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2013 Apple 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 IDBServerConnectionLevelDB_h
-#define IDBServerConnectionLevelDB_h
-
-#include "IDBServerConnection.h"
-
-#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
-
-namespace WebCore {
-
-class IDBBackingStoreCursorLevelDB;
-class IDBBackingStoreLevelDB;
-class IDBBackingStoreTransactionLevelDB;
-
-class IDBServerConnectionLevelDB final : public IDBServerConnection {
-public:
- static PassRefPtr<IDBServerConnection> create(const String& databaseName, IDBBackingStoreLevelDB* backingStore)
- {
- return adoptRef(new IDBServerConnectionLevelDB(databaseName, backingStore));
- }
-
- virtual ~IDBServerConnectionLevelDB();
-
- virtual bool isClosed() override;
-
- // Factory-level operations
- virtual void deleteDatabase(const String& name, BoolCallbackFunction successCallback) override;
-
- // Database-level operations
- virtual void getOrEstablishIDBDatabaseMetadata(GetIDBDatabaseMetadataFunction) override;
- virtual void close() override;
-
- // Transaction-level operations
- virtual void openTransaction(int64_t transactionID, const HashSet<int64_t>& objectStoreIds, IndexedDB::TransactionMode, BoolCallbackFunction successCallback) override;
- virtual void beginTransaction(int64_t transactionID, std::function<void()> completionCallback) override;
- virtual void commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback) override;
- virtual void resetTransaction(int64_t transactionID, std::function<void()> completionCallback) override;
- virtual void rollbackTransaction(int64_t transactionID, std::function<void()> completionCallback) override;
- virtual void setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata&, IDBKey& primaryKey, const Vector<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback);
-
- virtual void createObjectStore(IDBTransactionBackend&, const CreateObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void createIndex(IDBTransactionBackend&, const CreateIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void deleteIndex(IDBTransactionBackend&, const DeleteIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void get(IDBTransactionBackend&, const GetOperation&, std::function<void(const IDBGetResult&, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void put(IDBTransactionBackend&, const PutOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void openCursor(IDBTransactionBackend&, const OpenCursorOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void count(IDBTransactionBackend&, const CountOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void deleteRange(IDBTransactionBackend&, const DeleteRangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void clearObjectStore(IDBTransactionBackend&, const ClearObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void deleteObjectStore(IDBTransactionBackend&, const DeleteObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void changeDatabaseVersion(IDBTransactionBackend&, const IDBDatabaseBackend::VersionChangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
-
- // Cursor-level operations
- virtual void cursorAdvance(IDBCursorBackend&, const CursorAdvanceOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void cursorIterate(IDBCursorBackend&, const CursorIterationOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
-
-private:
- IDBServerConnectionLevelDB(const String& databaseName, IDBBackingStoreLevelDB*);
-
- RefPtr<IDBBackingStoreLevelDB> m_backingStore;
- HashMap<int64_t, RefPtr<IDBBackingStoreTransactionLevelDB>> m_backingStoreTransactions;
- HashMap<int64_t, RefPtr<IDBBackingStoreCursorLevelDB>> m_backingStoreCursors;
-
- int64_t m_nextCursorID;
-
- bool m_closed;
-
- String m_databaseName;
-};
-
-} // namespace WebCore
-
-#endif // USE(LEVELDB)
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBServerConnectionLevelDB_h
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h
new file mode 100644
index 000000000..9dde74c83
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBDatabaseInfo.h"
+#include "IDBError.h"
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBGetAllResult;
+class IDBGetResult;
+class IDBIndexInfo;
+class IDBKeyData;
+class IDBObjectStoreInfo;
+class IDBResourceIdentifier;
+class IDBTransactionInfo;
+class IDBValue;
+class ThreadSafeDataBuffer;
+
+enum class IDBGetRecordDataType;
+
+struct IDBGetAllRecordsData;
+struct IDBIterateCursorData;
+struct IDBKeyRangeData;
+
+namespace IndexedDB {
+enum class IndexRecordType;
+}
+
+namespace IDBServer {
+
+class IDBBackingStoreTemporaryFileHandler {
+public:
+ virtual ~IDBBackingStoreTemporaryFileHandler() { }
+ virtual void prepareForAccessToTemporaryFile(const String& path) = 0;
+ virtual void accessToTemporaryFileComplete(const String& path) = 0;
+};
+
+class IDBBackingStore {
+public:
+ virtual ~IDBBackingStore() { }
+
+ virtual IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) = 0;
+
+ virtual IDBError beginTransaction(const IDBTransactionInfo&) = 0;
+ virtual IDBError abortTransaction(const IDBResourceIdentifier& transactionIdentifier) = 0;
+ virtual IDBError commitTransaction(const IDBResourceIdentifier& transactionIdentifier) = 0;
+
+ virtual IDBError createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&) = 0;
+ virtual IDBError deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) = 0;
+ virtual IDBError renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) = 0;
+ virtual IDBError clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) = 0;
+ virtual IDBError createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&) = 0;
+ virtual IDBError deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) = 0;
+ virtual IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) = 0;
+ virtual IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) = 0;
+ virtual IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) = 0;
+ virtual IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) = 0;
+ virtual IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) = 0;
+ virtual IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) = 0;
+ virtual IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) = 0;
+ virtual IDBError getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, uint64_t& outCount) = 0;
+ virtual IDBError generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber) = 0;
+ virtual IDBError revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber) = 0;
+ virtual IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) = 0;
+ virtual IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) = 0;
+ virtual IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) = 0;
+ virtual bool prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier) = 0;
+
+ virtual IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) = 0;
+ virtual void deleteBackingStore() = 0;
+
+ virtual bool supportsSimultaneousTransactions() = 0;
+ virtual bool isEphemeral() = 0;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp
new file mode 100644
index 000000000..55bea5424
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBConnectionToClient.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "UniqueIDBDatabaseConnection.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<IDBConnectionToClient> IDBConnectionToClient::create(IDBConnectionToClientDelegate& delegate)
+{
+ return adoptRef(*new IDBConnectionToClient(delegate));
+}
+
+IDBConnectionToClient::IDBConnectionToClient(IDBConnectionToClientDelegate& delegate)
+ : m_delegate(delegate)
+{
+}
+
+uint64_t IDBConnectionToClient::identifier() const
+{
+ return m_delegate->identifier();
+}
+
+void IDBConnectionToClient::didDeleteDatabase(const IDBResultData& result)
+{
+ m_delegate->didDeleteDatabase(result);
+}
+
+void IDBConnectionToClient::didOpenDatabase(const IDBResultData& result)
+{
+ m_delegate->didOpenDatabase(result);
+}
+
+void IDBConnectionToClient::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ m_delegate->didAbortTransaction(transactionIdentifier, error);
+}
+
+void IDBConnectionToClient::didCreateObjectStore(const IDBResultData& result)
+{
+ m_delegate->didCreateObjectStore(result);
+}
+
+void IDBConnectionToClient::didDeleteObjectStore(const IDBResultData& result)
+{
+ m_delegate->didDeleteObjectStore(result);
+}
+
+void IDBConnectionToClient::didRenameObjectStore(const IDBResultData& result)
+{
+ m_delegate->didRenameObjectStore(result);
+}
+
+void IDBConnectionToClient::didClearObjectStore(const IDBResultData& result)
+{
+ m_delegate->didClearObjectStore(result);
+}
+
+void IDBConnectionToClient::didCreateIndex(const IDBResultData& result)
+{
+ m_delegate->didCreateIndex(result);
+}
+
+void IDBConnectionToClient::didDeleteIndex(const IDBResultData& result)
+{
+ m_delegate->didDeleteIndex(result);
+}
+
+void IDBConnectionToClient::didRenameIndex(const IDBResultData& result)
+{
+ m_delegate->didRenameIndex(result);
+}
+
+void IDBConnectionToClient::didPutOrAdd(const IDBResultData& result)
+{
+ m_delegate->didPutOrAdd(result);
+}
+
+void IDBConnectionToClient::didGetRecord(const IDBResultData& result)
+{
+ m_delegate->didGetRecord(result);
+}
+
+void IDBConnectionToClient::didGetAllRecords(const IDBResultData& result)
+{
+ m_delegate->didGetAllRecords(result);
+}
+
+void IDBConnectionToClient::didGetCount(const IDBResultData& result)
+{
+ m_delegate->didGetCount(result);
+}
+
+void IDBConnectionToClient::didDeleteRecord(const IDBResultData& result)
+{
+ m_delegate->didDeleteRecord(result);
+}
+
+void IDBConnectionToClient::didOpenCursor(const IDBResultData& result)
+{
+ m_delegate->didOpenCursor(result);
+}
+
+void IDBConnectionToClient::didIterateCursor(const IDBResultData& result)
+{
+ m_delegate->didIterateCursor(result);
+}
+
+void IDBConnectionToClient::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ m_delegate->didCommitTransaction(transactionIdentifier, error);
+}
+
+void IDBConnectionToClient::fireVersionChangeEvent(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion)
+{
+ m_delegate->fireVersionChangeEvent(connection, requestIdentifier, requestedVersion);
+}
+
+void IDBConnectionToClient::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ m_delegate->didStartTransaction(transactionIdentifier, error);
+}
+
+void IDBConnectionToClient::didCloseFromServer(UniqueIDBDatabaseConnection& connection, const IDBError& error)
+{
+ m_delegate->didCloseFromServer(connection, error);
+}
+
+void IDBConnectionToClient::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion)
+{
+ m_delegate->notifyOpenDBRequestBlocked(requestIdentifier, oldVersion, newVersion);
+}
+
+void IDBConnectionToClient::didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames)
+{
+ m_delegate->didGetAllDatabaseNames(callbackID, databaseNames);
+}
+
+void IDBConnectionToClient::registerDatabaseConnection(UniqueIDBDatabaseConnection& connection)
+{
+ ASSERT(!m_databaseConnections.contains(&connection));
+ m_databaseConnections.add(&connection);
+}
+
+void IDBConnectionToClient::unregisterDatabaseConnection(UniqueIDBDatabaseConnection& connection)
+{
+ m_databaseConnections.remove(&connection);
+}
+
+void IDBConnectionToClient::connectionToClientClosed()
+{
+ auto databaseConnections = m_databaseConnections;
+
+ for (auto connection : databaseConnections) {
+ if (m_databaseConnections.contains(connection))
+ connection->connectionClosedFromClient();
+ }
+
+ m_databaseConnections.clear();
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h
new file mode 100644
index 000000000..642813dd0
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToClientDelegate.h"
+#include <wtf/HashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBError;
+class IDBResourceIdentifier;
+class IDBResultData;
+
+namespace IDBServer {
+
+class UniqueIDBDatabaseConnection;
+
+class IDBConnectionToClient : public RefCounted<IDBConnectionToClient> {
+public:
+ WEBCORE_EXPORT static Ref<IDBConnectionToClient> create(IDBConnectionToClientDelegate&);
+
+ uint64_t identifier() const;
+
+ void didDeleteDatabase(const IDBResultData&);
+ void didOpenDatabase(const IDBResultData&);
+ void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+ void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+ void didCreateObjectStore(const IDBResultData&);
+ void didDeleteObjectStore(const IDBResultData&);
+ void didRenameObjectStore(const IDBResultData&);
+ void didClearObjectStore(const IDBResultData&);
+ void didCreateIndex(const IDBResultData&);
+ void didDeleteIndex(const IDBResultData&);
+ void didRenameIndex(const IDBResultData&);
+ void didPutOrAdd(const IDBResultData&);
+ void didGetRecord(const IDBResultData&);
+ void didGetAllRecords(const IDBResultData&);
+ void didGetCount(const IDBResultData&);
+ void didDeleteRecord(const IDBResultData&);
+ void didOpenCursor(const IDBResultData&);
+ void didIterateCursor(const IDBResultData&);
+
+ void fireVersionChangeEvent(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion);
+ void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&);
+ void didCloseFromServer(UniqueIDBDatabaseConnection&, const IDBError&);
+
+ void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion);
+
+ void didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames);
+
+ void registerDatabaseConnection(UniqueIDBDatabaseConnection&);
+ void unregisterDatabaseConnection(UniqueIDBDatabaseConnection&);
+ void connectionToClientClosed();
+
+private:
+ IDBConnectionToClient(IDBConnectionToClientDelegate&);
+
+ Ref<IDBConnectionToClientDelegate> m_delegate;
+ HashSet<UniqueIDBDatabaseConnection*> m_databaseConnections;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h
new file mode 100644
index 000000000..cc9465366
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class IDBError;
+class IDBResourceIdentifier;
+class IDBResultData;
+
+namespace IDBServer {
+
+class UniqueIDBDatabaseConnection;
+
+class IDBConnectionToClientDelegate {
+public:
+ virtual ~IDBConnectionToClientDelegate() { }
+
+ virtual uint64_t identifier() const = 0;
+
+ virtual void didDeleteDatabase(const IDBResultData&) = 0;
+ virtual void didOpenDatabase(const IDBResultData&) = 0;
+ virtual void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) = 0;
+ virtual void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) = 0;
+ virtual void didCreateObjectStore(const IDBResultData&) = 0;
+ virtual void didDeleteObjectStore(const IDBResultData&) = 0;
+ virtual void didRenameObjectStore(const IDBResultData&) = 0;
+ virtual void didClearObjectStore(const IDBResultData&) = 0;
+ virtual void didCreateIndex(const IDBResultData&) = 0;
+ virtual void didDeleteIndex(const IDBResultData&) = 0;
+ virtual void didRenameIndex(const IDBResultData&) = 0;
+ virtual void didPutOrAdd(const IDBResultData&) = 0;
+ virtual void didGetRecord(const IDBResultData&) = 0;
+ virtual void didGetAllRecords(const IDBResultData&) = 0;
+ virtual void didGetCount(const IDBResultData&) = 0;
+ virtual void didDeleteRecord(const IDBResultData&) = 0;
+ virtual void didOpenCursor(const IDBResultData&) = 0;
+ virtual void didIterateCursor(const IDBResultData&) = 0;
+
+ virtual void fireVersionChangeEvent(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) = 0;
+ virtual void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) = 0;
+ virtual void didCloseFromServer(UniqueIDBDatabaseConnection&, const IDBError&) = 0;
+ virtual void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) = 0;
+
+ virtual void didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames) = 0;
+
+ virtual void ref() = 0;
+ virtual void deref() = 0;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp
new file mode 100644
index 000000000..4806d5cc5
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2014, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBSerialization.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyData.h"
+#include "IDBKeyPath.h"
+#include "KeyedCoding.h"
+
+#if USE(GLIB)
+#include <glib.h>
+#include <wtf/glib/GRefPtr.h>
+#endif
+
+namespace WebCore {
+
+enum class KeyPathType { Null, String, Array };
+
+RefPtr<SharedBuffer> serializeIDBKeyPath(const std::optional<IDBKeyPath>& keyPath)
+{
+ auto encoder = KeyedEncoder::encoder();
+
+ if (keyPath) {
+ auto visitor = WTF::makeVisitor([&](const String& string) {
+ encoder->encodeEnum("type", KeyPathType::String);
+ encoder->encodeString("string", string);
+ }, [&](const Vector<String>& vector) {
+ encoder->encodeEnum("type", KeyPathType::Array);
+ encoder->encodeObjects("array", vector.begin(), vector.end(), [](WebCore::KeyedEncoder& encoder, const String& string) {
+ encoder.encodeString("string", string);
+ });
+ });
+ WTF::visit(visitor, keyPath.value());
+ } else
+ encoder->encodeEnum("type", KeyPathType::Null);
+
+ return encoder->finishEncoding();
+}
+
+bool deserializeIDBKeyPath(const uint8_t* data, size_t size, std::optional<IDBKeyPath>& result)
+{
+ if (!data || !size)
+ return false;
+
+ auto decoder = KeyedDecoder::decoder(data, size);
+
+ KeyPathType type;
+ bool succeeded = decoder->decodeEnum("type", type, [](KeyPathType value) {
+ return value == KeyPathType::Null || value == KeyPathType::String || value == KeyPathType::Array;
+ });
+ if (!succeeded)
+ return false;
+
+ switch (type) {
+ case KeyPathType::Null:
+ break;
+ case KeyPathType::String: {
+ String string;
+ if (!decoder->decodeString("string", string))
+ return false;
+ result = IDBKeyPath(WTFMove(string));
+ break;
+ }
+ case KeyPathType::Array: {
+ Vector<String> vector;
+ succeeded = decoder->decodeObjects("array", vector, [](KeyedDecoder& decoder, String& result) {
+ return decoder.decodeString("string", result);
+ });
+ if (!succeeded)
+ return false;
+ result = IDBKeyPath(WTFMove(vector));
+ break;
+ }
+ }
+ return true;
+}
+
+static bool isLegacySerializedIDBKeyData(const uint8_t* data, size_t size)
+{
+#if USE(CF)
+ UNUSED_PARAM(size);
+
+ // This is the magic character that begins serialized PropertyLists, and tells us whether
+ // the key we're looking at is an old-style key.
+ static const uint8_t legacySerializedKeyVersion = 'b';
+ if (data[0] == legacySerializedKeyVersion)
+ return true;
+#elif USE(GLIB)
+ // KeyedEncoderGLib uses a GVariant dictionary, so check if the given data is a valid GVariant dictionary.
+ GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new(data, size));
+ GRefPtr<GVariant> variant = g_variant_new_from_bytes(G_VARIANT_TYPE("a{sv}"), bytes.get(), FALSE);
+ return g_variant_is_normal_form(variant.get());
+#endif
+ return false;
+}
+
+
+/*
+The IDBKeyData serialization format is as follows:
+[1 byte version header][Key Buffer]
+
+The Key Buffer serialization format is as follows:
+[1 byte key type][Type specific data]
+
+Type specific serialization formats are as follows for each of the types:
+Min:
+[0 bytes]
+
+Number:
+[8 bytes representing a double encoded in little endian]
+
+Date:
+[8 bytes representing a double encoded in little endian]
+
+String:
+[4 bytes representing string "length" in little endian]["length" number of 2-byte pairs representing ECMAScript 16-bit code units]
+
+Binary:
+[8 bytes representing the "size" of the binary blob]["size" bytes]
+
+Array:
+[8 bytes representing the "length" of the key array]["length" individual Key Buffer entries]
+
+Max:
+[0 bytes]
+*/
+
+static const uint8_t SIDBKeyVersion = 0x00;
+enum class SIDBKeyType : uint8_t {
+ Min = 0x00,
+ Number = 0x20,
+ Date = 0x40,
+ String = 0x60,
+ Binary = 0x80,
+ Array = 0xA0,
+ Max = 0xFF,
+};
+
+static SIDBKeyType serializedTypeForKeyType(IndexedDB::KeyType type)
+{
+ switch (type) {
+ case IndexedDB::KeyType::Min:
+ return SIDBKeyType::Min;
+ case IndexedDB::KeyType::Number:
+ return SIDBKeyType::Number;
+ case IndexedDB::KeyType::Date:
+ return SIDBKeyType::Date;
+ case IndexedDB::KeyType::String:
+ return SIDBKeyType::String;
+ case IndexedDB::KeyType::Binary:
+ return SIDBKeyType::Binary;
+ case IndexedDB::KeyType::Array:
+ return SIDBKeyType::Array;
+ case IndexedDB::KeyType::Max:
+ return SIDBKeyType::Max;
+ case IndexedDB::KeyType::Invalid:
+ RELEASE_ASSERT_NOT_REACHED();
+ };
+
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
+template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
+{
+ for (unsigned i = 0; i < sizeof(T); i++) {
+ buffer.append(value & 0xFF);
+ value >>= 8;
+ }
+}
+
+template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
+{
+ if (ptr > end - sizeof(value))
+ return false;
+
+ value = 0;
+ for (size_t i = 0; i < sizeof(T); i++)
+ value += ((T)*ptr++) << (i * 8);
+ return true;
+}
+#else
+template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
+{
+ buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+}
+
+template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
+{
+ if (ptr > end - sizeof(value))
+ return false;
+
+ value = *reinterpret_cast<const T*>(ptr);
+ ptr += sizeof(T);
+
+ return true;
+}
+#endif
+
+static void writeDouble(Vector<char>& data, double d)
+{
+ writeLittleEndian(data, *reinterpret_cast<uint64_t*>(&d));
+}
+
+static bool readDouble(const uint8_t*& data, const uint8_t* end, double& d)
+{
+ return readLittleEndian(data, end, *reinterpret_cast<uint64_t*>(&d));
+}
+
+static void encodeKey(Vector<char>& data, const IDBKeyData& key)
+{
+ SIDBKeyType type = serializedTypeForKeyType(key.type());
+ data.append(static_cast<char>(type));
+
+ switch (type) {
+ case SIDBKeyType::Number:
+ writeDouble(data, key.number());
+ break;
+ case SIDBKeyType::Date:
+ writeDouble(data, key.date());
+ break;
+ case SIDBKeyType::String: {
+ auto string = key.string();
+ uint32_t length = string.length();
+ writeLittleEndian(data, length);
+
+ for (size_t i = 0; i < length; ++i)
+ writeLittleEndian(data, string[i]);
+
+ break;
+ }
+ case SIDBKeyType::Binary: {
+ auto& buffer = key.binary();
+ uint64_t size = buffer.size();
+ writeLittleEndian(data, size);
+
+ auto* bufferData = buffer.data();
+ ASSERT(bufferData || !size);
+ if (bufferData)
+ data.append(bufferData->data(), bufferData->size());
+
+ break;
+ }
+ case SIDBKeyType::Array: {
+ auto& array = key.array();
+ uint64_t size = array.size();
+ writeLittleEndian(data, size);
+ for (auto& key : array)
+ encodeKey(data, key);
+
+ break;
+ }
+ case SIDBKeyType::Min:
+ case SIDBKeyType::Max:
+ break;
+ }
+}
+
+RefPtr<SharedBuffer> serializeIDBKeyData(const IDBKeyData& key)
+{
+ Vector<char> data;
+ data.append(SIDBKeyVersion);
+
+ encodeKey(data, key);
+ return SharedBuffer::adoptVector(data);
+}
+
+static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result)
+{
+ if (!data || data >= end)
+ return false;
+
+ SIDBKeyType type = static_cast<SIDBKeyType>(data++[0]);
+ switch (type) {
+ case SIDBKeyType::Min:
+ result = IDBKeyData::minimum();
+ return true;
+ case SIDBKeyType::Max:
+ result = IDBKeyData::maximum();
+ return true;
+ case SIDBKeyType::Number: {
+ double d;
+ if (!readDouble(data, end, d))
+ return false;
+
+ result.setNumberValue(d);
+ return true;
+ }
+ case SIDBKeyType::Date: {
+ double d;
+ if (!readDouble(data, end, d))
+ return false;
+
+ result.setDateValue(d);
+ return true;
+ }
+ case SIDBKeyType::String: {
+ uint32_t length;
+ if (!readLittleEndian(data, end, length))
+ return false;
+
+ if (static_cast<uint64_t>(end - data) < length * 2)
+ return false;
+
+ Vector<UChar> buffer;
+ buffer.reserveInitialCapacity(length);
+ for (size_t i = 0; i < length; i++) {
+ uint16_t ch;
+ if (!readLittleEndian(data, end, ch))
+ return false;
+ buffer.uncheckedAppend(ch);
+ }
+
+ result.setStringValue(String::adopt(WTFMove(buffer)));
+
+ return true;
+ }
+ case SIDBKeyType::Binary: {
+ uint64_t size64;
+ if (!readLittleEndian(data, end, size64))
+ return false;
+
+ if (static_cast<uint64_t>(end - data) < size64)
+ return false;
+
+ if (size64 > std::numeric_limits<size_t>::max())
+ return false;
+
+ size_t size = static_cast<size_t>(size64);
+ Vector<uint8_t> dataVector;
+
+ dataVector.append(data, size);
+ data += size;
+
+ result.setBinaryValue(ThreadSafeDataBuffer::adoptVector(dataVector));
+ return true;
+ }
+ case SIDBKeyType::Array: {
+ uint64_t size64;
+ if (!readLittleEndian(data, end, size64))
+ return false;
+
+ if (size64 > std::numeric_limits<size_t>::max())
+ return false;
+
+ size_t size = static_cast<size_t>(size64);
+ Vector<IDBKeyData> array;
+ array.reserveInitialCapacity(size);
+
+ for (size_t i = 0; i < size; ++i) {
+ IDBKeyData keyData;
+ if (!decodeKey(data, end, keyData))
+ return false;
+
+ ASSERT(keyData.isValid());
+ array.uncheckedAppend(WTFMove(keyData));
+ }
+
+ result.setArrayValue(array);
+
+ return true;
+ }
+ default:
+ LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type);
+ return false;
+ }
+}
+
+bool deserializeIDBKeyData(const uint8_t* data, size_t size, IDBKeyData& result)
+{
+ if (!data || !size)
+ return false;
+
+ if (isLegacySerializedIDBKeyData(data, size)) {
+ auto decoder = KeyedDecoder::decoder(data, size);
+ return IDBKeyData::decode(*decoder, result);
+ }
+
+ // Verify this is a SerializedIDBKey version we understand.
+ const uint8_t* current = data;
+ const uint8_t* end = data + size;
+ if (current++[0] != SIDBKeyVersion)
+ return false;
+
+ if (decodeKey(current, end, result)) {
+ // Even if we successfully decoded a key, the deserialize is only successful
+ // if we actually consumed all input data.
+ return current == end;
+ }
+
+ return false;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBSerialization.h b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.h
new file mode 100644
index 000000000..094fbc6ec
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyPath.h"
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+class IDBKeyData;
+
+RefPtr<SharedBuffer> serializeIDBKeyPath(const std::optional<IDBKeyPath>&);
+bool deserializeIDBKeyPath(const uint8_t* buffer, size_t bufferSize, std::optional<IDBKeyPath>&);
+
+RefPtr<SharedBuffer> serializeIDBKeyData(const IDBKeyData&);
+bool deserializeIDBKeyData(const uint8_t* buffer, size_t bufferSize, IDBKeyData&);
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
new file mode 100644
index 000000000..87476438c
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
@@ -0,0 +1,682 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBServer.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBRequestData.h"
+#include "IDBResultData.h"
+#include "Logging.h"
+#include "MemoryIDBBackingStore.h"
+#include "SQLiteFileSystem.h"
+#include "SQLiteIDBBackingStore.h"
+#include "SecurityOrigin.h"
+#include <wtf/CrossThreadCopier.h>
+#include <wtf/Locker.h>
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<IDBServer> IDBServer::create(IDBBackingStoreTemporaryFileHandler& fileHandler)
+{
+ return adoptRef(*new IDBServer(fileHandler));
+}
+
+Ref<IDBServer> IDBServer::create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler)
+{
+ return adoptRef(*new IDBServer(databaseDirectoryPath, fileHandler));
+}
+
+IDBServer::IDBServer(IDBBackingStoreTemporaryFileHandler& fileHandler)
+ : m_backingStoreTemporaryFileHandler(fileHandler)
+{
+ Locker<Lock> locker(m_databaseThreadCreationLock);
+ m_threadID = createThread(IDBServer::databaseThreadEntry, this, "IndexedDatabase Server");
+}
+
+IDBServer::IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler)
+ : m_databaseDirectoryPath(databaseDirectoryPath)
+ , m_backingStoreTemporaryFileHandler(fileHandler)
+{
+ LOG(IndexedDB, "IDBServer created at path %s", databaseDirectoryPath.utf8().data());
+
+ Locker<Lock> locker(m_databaseThreadCreationLock);
+ m_threadID = createThread(IDBServer::databaseThreadEntry, this, "IndexedDatabase Server");
+}
+
+void IDBServer::registerConnection(IDBConnectionToClient& connection)
+{
+ ASSERT(!m_connectionMap.contains(connection.identifier()));
+ m_connectionMap.set(connection.identifier(), &connection);
+}
+
+void IDBServer::unregisterConnection(IDBConnectionToClient& connection)
+{
+ ASSERT(m_connectionMap.contains(connection.identifier()));
+ ASSERT(m_connectionMap.get(connection.identifier()) == &connection);
+
+ connection.connectionToClientClosed();
+
+ m_connectionMap.remove(connection.identifier());
+}
+
+void IDBServer::registerTransaction(UniqueIDBDatabaseTransaction& transaction)
+{
+ ASSERT(!m_transactions.contains(transaction.info().identifier()));
+ m_transactions.set(transaction.info().identifier(), &transaction);
+}
+
+void IDBServer::unregisterTransaction(UniqueIDBDatabaseTransaction& transaction)
+{
+ ASSERT(m_transactions.contains(transaction.info().identifier()));
+ ASSERT(m_transactions.get(transaction.info().identifier()) == &transaction);
+
+ m_transactions.remove(transaction.info().identifier());
+}
+
+void IDBServer::registerDatabaseConnection(UniqueIDBDatabaseConnection& connection)
+{
+ ASSERT(!m_databaseConnections.contains(connection.identifier()));
+ m_databaseConnections.set(connection.identifier(), &connection);
+}
+
+void IDBServer::unregisterDatabaseConnection(UniqueIDBDatabaseConnection& connection)
+{
+ ASSERT(m_databaseConnections.contains(connection.identifier()));
+ m_databaseConnections.remove(connection.identifier());
+}
+
+UniqueIDBDatabase& IDBServer::getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier& identifier)
+{
+ ASSERT(isMainThread());
+
+ auto uniqueIDBDatabase = m_uniqueIDBDatabaseMap.add(identifier, nullptr);
+ if (uniqueIDBDatabase.isNewEntry)
+ uniqueIDBDatabase.iterator->value = UniqueIDBDatabase::create(*this, identifier);
+
+ return *uniqueIDBDatabase.iterator->value;
+}
+
+std::unique_ptr<IDBBackingStore> IDBServer::createBackingStore(const IDBDatabaseIdentifier& identifier)
+{
+ ASSERT(!isMainThread());
+
+ if (m_databaseDirectoryPath.isEmpty())
+ return MemoryIDBBackingStore::create(identifier);
+
+ return std::make_unique<SQLiteIDBBackingStore>(identifier, m_databaseDirectoryPath, m_backingStoreTemporaryFileHandler);
+}
+
+void IDBServer::openDatabase(const IDBRequestData& requestData)
+{
+ LOG(IndexedDB, "IDBServer::openDatabase");
+
+ auto& uniqueIDBDatabase = getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier());
+
+ auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier());
+ if (!connection) {
+ // If the connection back to the client is gone, there's no way to open the database as
+ // well as no way to message back failure.
+ return;
+ }
+
+ uniqueIDBDatabase.openDatabaseConnection(*connection, requestData);
+}
+
+void IDBServer::deleteDatabase(const IDBRequestData& requestData)
+{
+ LOG(IndexedDB, "IDBServer::deleteDatabase - %s", requestData.databaseIdentifier().debugString().utf8().data());
+ ASSERT(isMainThread());
+
+ auto connection = m_connectionMap.get(requestData.requestIdentifier().connectionIdentifier());
+ if (!connection) {
+ // If the connection back to the client is gone, there's no way to delete the database as
+ // well as no way to message back failure.
+ return;
+ }
+
+ auto* database = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier());
+ if (!database)
+ database = &getOrCreateUniqueIDBDatabase(requestData.databaseIdentifier());
+
+ database->handleDelete(*connection, requestData);
+}
+
+void IDBServer::closeUniqueIDBDatabase(UniqueIDBDatabase& database)
+{
+ LOG(IndexedDB, "IDBServer::closeUniqueIDBDatabase");
+ ASSERT(isMainThread());
+
+ m_uniqueIDBDatabaseMap.remove(database.identifier());
+}
+
+void IDBServer::abortTransaction(const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::abortTransaction");
+
+ auto transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction) {
+ // If there is no transaction there is nothing to abort.
+ // We also have no access to a connection over which to message failure-to-abort.
+ return;
+ }
+
+ transaction->abort();
+}
+
+void IDBServer::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info)
+{
+ LOG(IndexedDB, "IDBServer::createObjectStore");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ ASSERT(transaction->isVersionChange());
+ transaction->createObjectStore(requestData, info);
+}
+
+void IDBServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName)
+{
+ LOG(IndexedDB, "IDBServer::deleteObjectStore");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ ASSERT(transaction->isVersionChange());
+ transaction->deleteObjectStore(requestData, objectStoreName);
+}
+
+void IDBServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "IDBServer::renameObjectStore");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ ASSERT(transaction->isVersionChange());
+ transaction->renameObjectStore(requestData, objectStoreIdentifier, newName);
+}
+
+void IDBServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::clearObjectStore");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->clearObjectStore(requestData, objectStoreIdentifier);
+}
+
+void IDBServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "IDBServer::createIndex");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ ASSERT(transaction->isVersionChange());
+ transaction->createIndex(requestData, info);
+}
+
+void IDBServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName)
+{
+ LOG(IndexedDB, "IDBServer::deleteIndex");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ ASSERT(transaction->isVersionChange());
+ transaction->deleteIndex(requestData, objectStoreIdentifier, indexName);
+}
+
+void IDBServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "IDBServer::renameIndex");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ ASSERT(transaction->isVersionChange());
+ transaction->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName);
+}
+
+void IDBServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
+{
+ LOG(IndexedDB, "IDBServer::putOrAdd");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->putOrAdd(requestData, keyData, value, overwriteMode);
+}
+
+void IDBServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData)
+{
+ LOG(IndexedDB, "IDBServer::getRecord");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->getRecord(requestData, getRecordData);
+}
+
+void IDBServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData)
+{
+ LOG(IndexedDB, "IDBServer::getAllRecords");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->getAllRecords(requestData, getAllRecordsData);
+}
+
+void IDBServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ LOG(IndexedDB, "IDBServer::getCount");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->getCount(requestData, keyRangeData);
+}
+
+void IDBServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ LOG(IndexedDB, "IDBServer::deleteRecord");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->deleteRecord(requestData, keyRangeData);
+}
+
+void IDBServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info)
+{
+ LOG(IndexedDB, "IDBServer::openCursor");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->openCursor(requestData, info);
+}
+
+void IDBServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data)
+{
+ LOG(IndexedDB, "IDBServer::iterateCursor");
+
+ auto transaction = m_transactions.get(requestData.transactionIdentifier());
+ if (!transaction)
+ return;
+
+ transaction->iterateCursor(requestData, data);
+}
+
+void IDBServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info)
+{
+ LOG(IndexedDB, "IDBServer::establishTransaction");
+
+ auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
+ if (!databaseConnection)
+ return;
+
+ databaseConnection->establishTransaction(info);
+}
+
+void IDBServer::commitTransaction(const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::commitTransaction");
+
+ auto transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction) {
+ // If there is no transaction there is nothing to commit.
+ // We also have no access to a connection over which to message failure-to-commit.
+ return;
+ }
+
+ transaction->commit();
+}
+
+void IDBServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::didFinishHandlingVersionChangeTransaction - %s", transactionIdentifier.loggingString().utf8().data());
+
+ auto* connection = m_databaseConnections.get(databaseConnectionIdentifier);
+ if (!connection)
+ return;
+
+ connection->didFinishHandlingVersionChange(transactionIdentifier);
+}
+
+void IDBServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::databaseConnectionPendingClose - %" PRIu64, databaseConnectionIdentifier);
+
+ auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
+ if (!databaseConnection)
+ return;
+
+ databaseConnection->connectionPendingCloseFromClient();
+}
+
+void IDBServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::databaseConnectionClosed - %" PRIu64, databaseConnectionIdentifier);
+
+ auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
+ if (!databaseConnection)
+ return;
+
+ databaseConnection->connectionClosedFromClient();
+}
+
+void IDBServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::abortOpenAndUpgradeNeeded");
+
+ auto transaction = m_transactions.get(transactionIdentifier);
+ if (transaction)
+ transaction->abortWithoutCallback();
+
+ auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier);
+ if (!databaseConnection)
+ return;
+
+ databaseConnection->connectionClosedFromClient();
+}
+
+void IDBServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::didFireVersionChangeEvent");
+
+ if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier))
+ databaseConnection->didFireVersionChangeEvent(requestIdentifier);
+}
+
+void IDBServer::openDBRequestCancelled(const IDBRequestData& requestData)
+{
+ LOG(IndexedDB, "IDBServer::openDBRequestCancelled");
+ ASSERT(isMainThread());
+
+ auto* uniqueIDBDatabase = m_uniqueIDBDatabaseMap.get(requestData.databaseIdentifier());
+ if (!uniqueIDBDatabase)
+ return;
+
+ uniqueIDBDatabase->openDBRequestCancelled(requestData.requestIdentifier());
+}
+
+void IDBServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier)
+{
+ LOG(IndexedDB, "IDBServer::confirmDidCloseFromServer");
+
+ if (auto databaseConnection = m_databaseConnections.get(databaseConnectionIdentifier))
+ databaseConnection->confirmDidCloseFromServer();
+}
+
+void IDBServer::getAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID)
+{
+ postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performGetAllDatabaseNames, serverConnectionIdentifier, mainFrameOrigin, openingOrigin, callbackID));
+}
+
+void IDBServer::performGetAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID)
+{
+ String directory = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(mainFrameOrigin, openingOrigin, m_databaseDirectoryPath);
+
+ Vector<String> entries = listDirectory(directory, ASCIILiteral("*"));
+ Vector<String> databases;
+ databases.reserveInitialCapacity(entries.size());
+ for (auto& entry : entries) {
+ String encodedName = lastComponentOfPathIgnoringTrailingSlash(entry);
+ databases.uncheckedAppend(SQLiteIDBBackingStore::databaseNameFromEncodedFilename(encodedName));
+ }
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didGetAllDatabaseNames, serverConnectionIdentifier, callbackID, databases));
+}
+
+void IDBServer::didGetAllDatabaseNames(uint64_t serverConnectionIdentifier, uint64_t callbackID, const Vector<String>& databaseNames)
+{
+ auto connection = m_connectionMap.get(serverConnectionIdentifier);
+ if (!connection)
+ return;
+
+ connection->didGetAllDatabaseNames(callbackID, databaseNames);
+}
+
+void IDBServer::postDatabaseTask(CrossThreadTask&& task)
+{
+ m_databaseQueue.append(WTFMove(task));
+}
+
+void IDBServer::postDatabaseTaskReply(CrossThreadTask&& task)
+{
+ ASSERT(!isMainThread());
+ m_databaseReplyQueue.append(WTFMove(task));
+
+
+ Locker<Lock> locker(m_mainThreadReplyLock);
+ if (m_mainThreadReplyScheduled)
+ return;
+
+ m_mainThreadReplyScheduled = true;
+ callOnMainThread([this] {
+ handleTaskRepliesOnMainThread();
+ });
+}
+
+void IDBServer::databaseThreadEntry(void* threadData)
+{
+ ASSERT(threadData);
+ IDBServer* server = reinterpret_cast<IDBServer*>(threadData);
+ server->databaseRunLoop();
+}
+
+void IDBServer::databaseRunLoop()
+{
+ ASSERT(!isMainThread());
+ {
+ Locker<Lock> locker(m_databaseThreadCreationLock);
+ }
+
+ while (!m_databaseQueue.isKilled())
+ m_databaseQueue.waitForMessage().performTask();
+}
+
+void IDBServer::handleTaskRepliesOnMainThread()
+{
+ {
+ Locker<Lock> locker(m_mainThreadReplyLock);
+ m_mainThreadReplyScheduled = false;
+ }
+
+ while (auto task = m_databaseReplyQueue.tryGetMessage())
+ task->performTask();
+}
+
+static uint64_t generateDeleteCallbackID()
+{
+ ASSERT(isMainThread());
+ static uint64_t currentID = 0;
+ return ++currentID;
+}
+
+void IDBServer::closeAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point modificationTime, std::function<void ()> completionHandler)
+{
+ uint64_t callbackID = generateDeleteCallbackID();
+ auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler));
+ ASSERT_UNUSED(addResult, addResult.isNewEntry);
+
+ // If the modification time is in the future, don't both doing anything.
+ if (modificationTime > std::chrono::system_clock::now()) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
+ return;
+ }
+
+ HashSet<RefPtr<UniqueIDBDatabase>> openDatabases;
+ for (auto* connection : m_databaseConnections.values())
+ openDatabases.add(&connection->database());
+
+ for (auto& database : openDatabases)
+ database->immediateCloseForUserDelete();
+
+ postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesModifiedSince, modificationTime, callbackID));
+}
+
+void IDBServer::closeAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& origins, std::function<void ()> completionHandler)
+{
+ uint64_t callbackID = generateDeleteCallbackID();
+ auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler));
+ ASSERT_UNUSED(addResult, addResult.isNewEntry);
+
+ HashSet<RefPtr<UniqueIDBDatabase>> openDatabases;
+ for (auto* connection : m_databaseConnections.values()) {
+ const auto& identifier = connection->database().identifier();
+ for (auto& origin : origins) {
+ if (identifier.isRelatedToOrigin(origin)) {
+ openDatabases.add(&connection->database());
+ break;
+ }
+ }
+ }
+
+ for (auto& database : openDatabases)
+ database->immediateCloseForUserDelete();
+
+ postDatabaseTask(createCrossThreadTask(*this, &IDBServer::performCloseAndDeleteDatabasesForOrigins, origins, callbackID));
+}
+
+static void removeAllDatabasesForOriginPath(const String& originPath, std::chrono::system_clock::time_point modifiedSince)
+{
+ Vector<String> databasePaths = listDirectory(originPath, "*");
+
+ for (auto& databasePath : databasePaths) {
+ String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3");
+
+ if (modifiedSince > std::chrono::system_clock::time_point::min() && fileExists(databaseFile)) {
+ time_t modificationTime;
+ if (!getFileModificationTime(databaseFile, modificationTime))
+ continue;
+
+ if (std::chrono::system_clock::from_time_t(modificationTime) < modifiedSince)
+ continue;
+ }
+
+ // Deleting this database means we need to delete all files that represent it.
+ // This includes:
+ // - The directory itself, which is named after the database.
+ // - IndexedDB.sqlite3 and related SQLite files.
+ // - Blob files that we stored in the directory.
+ //
+ // To be conservative, we should *not* try to delete files that are unexpected;
+ // We should only delete files we think we put there.
+ //
+ // IndexedDB blob files are named "N.blob" where N is a decimal integer,
+ // so those are the only blob files we should be trying to delete.
+ for (auto& blobPath : listDirectory(databasePath, "[0-9]*.blob")) {
+ // Globbing can't give us only filenames starting with 1-or-more digits.
+ // The above globbing gives us files that start with a digit and ends with ".blob", but there might be non-digits in between.
+ // We need to validate that each filename contains only digits before deleting it, as any other files are not ones we put there.
+ String filename = pathGetFileName(blobPath);
+ auto filenameLength = filename.length();
+
+ ASSERT(filenameLength >= 6);
+ ASSERT(filename.endsWith(".blob"));
+
+ if (filename.length() < 6)
+ continue;
+ if (!filename.endsWith(".blob"))
+ continue;
+
+ bool validFilename = true;
+ for (unsigned i = 0; i < filenameLength - 5; ++i) {
+ if (!isASCIIDigit(filename[i])) {
+ validFilename = false;
+ break;
+ }
+ }
+
+ if (validFilename)
+ deleteFile(blobPath);
+ }
+
+ // Now delete IndexedDB.sqlite3 and related SQLite files.
+ SQLiteFileSystem::deleteDatabaseFile(databaseFile);
+
+ // And finally, if we can, delete the empty directory.
+ deleteEmptyDirectory(databasePath);
+ }
+
+ // If no databases remain for this origin, we can delete the origin directory as well.
+ deleteEmptyDirectory(originPath);
+}
+
+void IDBServer::performCloseAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID)
+{
+ if (!m_databaseDirectoryPath.isEmpty()) {
+ Vector<String> originPaths = listDirectory(m_databaseDirectoryPath, "*");
+ for (auto& originPath : originPaths)
+ removeAllDatabasesForOriginPath(originPath, modifiedSince);
+ }
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
+}
+
+void IDBServer::performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& origins, uint64_t callbackID)
+{
+ if (!m_databaseDirectoryPath.isEmpty()) {
+ for (const auto& origin : origins) {
+ String originPath = pathByAppendingComponent(m_databaseDirectoryPath, origin.databaseIdentifier());
+ removeAllDatabasesForOriginPath(originPath, std::chrono::system_clock::time_point::min());
+ }
+ }
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
+}
+
+void IDBServer::didPerformCloseAndDeleteDatabases(uint64_t callbackID)
+{
+ auto callback = m_deleteDatabaseCompletionHandlers.take(callbackID);
+ ASSERT(callback);
+ callback();
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.h b/Source/WebCore/Modules/indexeddb/server/IDBServer.h
new file mode 100644
index 000000000..256cd507e
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToClient.h"
+#include "IDBDatabaseIdentifier.h"
+#include "UniqueIDBDatabase.h"
+#include "UniqueIDBDatabaseConnection.h"
+#include <wtf/CrossThreadQueue.h>
+#include <wtf/CrossThreadTask.h>
+#include <wtf/HashMap.h>
+#include <wtf/Lock.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBRequestData;
+class IDBValue;
+
+struct IDBGetRecordData;
+
+namespace IDBServer {
+
+class IDBBackingStoreTemporaryFileHandler;
+
+class IDBServer : public RefCounted<IDBServer> {
+public:
+ static Ref<IDBServer> create(IDBBackingStoreTemporaryFileHandler&);
+ WEBCORE_EXPORT static Ref<IDBServer> create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&);
+
+ WEBCORE_EXPORT void registerConnection(IDBConnectionToClient&);
+ WEBCORE_EXPORT void unregisterConnection(IDBConnectionToClient&);
+
+ // Operations requested by the client.
+ WEBCORE_EXPORT void openDatabase(const IDBRequestData&);
+ WEBCORE_EXPORT void deleteDatabase(const IDBRequestData&);
+ WEBCORE_EXPORT void abortTransaction(const IDBResourceIdentifier&);
+ WEBCORE_EXPORT void commitTransaction(const IDBResourceIdentifier&);
+ WEBCORE_EXPORT void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&);
+ WEBCORE_EXPORT void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&);
+ WEBCORE_EXPORT void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName);
+ WEBCORE_EXPORT void deleteObjectStore(const IDBRequestData&, const String& objectStoreName);
+ WEBCORE_EXPORT void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier);
+ WEBCORE_EXPORT void createIndex(const IDBRequestData&, const IDBIndexInfo&);
+ WEBCORE_EXPORT void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName);
+ WEBCORE_EXPORT void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName);
+ WEBCORE_EXPORT void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode);
+ WEBCORE_EXPORT void getRecord(const IDBRequestData&, const IDBGetRecordData&);
+ WEBCORE_EXPORT void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&);
+ WEBCORE_EXPORT void getCount(const IDBRequestData&, const IDBKeyRangeData&);
+ WEBCORE_EXPORT void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&);
+ WEBCORE_EXPORT void openCursor(const IDBRequestData&, const IDBCursorInfo&);
+ WEBCORE_EXPORT void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&);
+
+ WEBCORE_EXPORT void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&);
+ WEBCORE_EXPORT void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier);
+ WEBCORE_EXPORT void databaseConnectionClosed(uint64_t databaseConnectionIdentifier);
+ WEBCORE_EXPORT void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier);
+ WEBCORE_EXPORT void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier);
+ WEBCORE_EXPORT void openDBRequestCancelled(const IDBRequestData&);
+ WEBCORE_EXPORT void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier);
+
+ WEBCORE_EXPORT void getAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID);
+
+ void postDatabaseTask(CrossThreadTask&&);
+ void postDatabaseTaskReply(CrossThreadTask&&);
+
+ void registerDatabaseConnection(UniqueIDBDatabaseConnection&);
+ void unregisterDatabaseConnection(UniqueIDBDatabaseConnection&);
+ void registerTransaction(UniqueIDBDatabaseTransaction&);
+ void unregisterTransaction(UniqueIDBDatabaseTransaction&);
+
+ void closeUniqueIDBDatabase(UniqueIDBDatabase&);
+
+ std::unique_ptr<IDBBackingStore> createBackingStore(const IDBDatabaseIdentifier&);
+
+ WEBCORE_EXPORT void closeAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point, std::function<void ()> completionHandler);
+ WEBCORE_EXPORT void closeAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>&, std::function<void ()> completionHandler);
+
+private:
+ IDBServer(IDBBackingStoreTemporaryFileHandler&);
+ IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&);
+
+ UniqueIDBDatabase& getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier&);
+
+ void performGetAllDatabaseNames(uint64_t serverConnectionIdentifier, const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID);
+ void didGetAllDatabaseNames(uint64_t serverConnectionIdentifier, uint64_t callbackID, const Vector<String>& databaseNames);
+
+ void performCloseAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point, uint64_t callbackID);
+ void performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>&, uint64_t callbackID);
+ void didPerformCloseAndDeleteDatabases(uint64_t callbackID);
+
+ static void databaseThreadEntry(void*);
+ void databaseRunLoop();
+ void handleTaskRepliesOnMainThread();
+
+ HashMap<uint64_t, RefPtr<IDBConnectionToClient>> m_connectionMap;
+ HashMap<IDBDatabaseIdentifier, RefPtr<UniqueIDBDatabase>> m_uniqueIDBDatabaseMap;
+
+ ThreadIdentifier m_threadID { 0 };
+ Lock m_databaseThreadCreationLock;
+ Lock m_mainThreadReplyLock;
+ bool m_mainThreadReplyScheduled { false };
+
+ CrossThreadQueue<CrossThreadTask> m_databaseQueue;
+ CrossThreadQueue<CrossThreadTask> m_databaseReplyQueue;
+
+ HashMap<uint64_t, UniqueIDBDatabaseConnection*> m_databaseConnections;
+ HashMap<IDBResourceIdentifier, UniqueIDBDatabaseTransaction*> m_transactions;
+
+ HashMap<uint64_t, std::function<void ()>> m_deleteDatabaseCompletionHandlers;
+
+ String m_databaseDirectoryPath;
+ IDBBackingStoreTemporaryFileHandler& m_backingStoreTemporaryFileHandler;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp
new file mode 100644
index 000000000..38c860677
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "IndexValueEntry.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+IndexValueEntry::IndexValueEntry(bool unique)
+ : m_unique(unique)
+{
+ if (m_unique)
+ m_key = nullptr;
+ else
+ m_orderedKeys = new std::set<IDBKeyData>;
+}
+
+IndexValueEntry::~IndexValueEntry()
+{
+ if (m_unique)
+ delete m_key;
+ else
+ delete m_orderedKeys;
+}
+
+void IndexValueEntry::addKey(const IDBKeyData& key)
+{
+ if (m_unique) {
+ delete m_key;
+ m_key = new IDBKeyData(key);
+ return;
+ }
+
+ m_orderedKeys->insert(key);
+}
+
+bool IndexValueEntry::removeKey(const IDBKeyData& key)
+{
+ if (m_unique) {
+ if (m_key && *m_key == key) {
+ delete m_key;
+ m_key = nullptr;
+ return true;
+ }
+
+ return false;
+ }
+
+ return m_orderedKeys->erase(key);
+}
+
+const IDBKeyData* IndexValueEntry::getLowest() const
+{
+ if (m_unique)
+ return m_key;
+
+ if (m_orderedKeys->empty())
+ return nullptr;
+
+ return &(*m_orderedKeys->begin());
+}
+
+uint64_t IndexValueEntry::getCount() const
+{
+ if (m_unique)
+ return m_key ? 1 : 0;
+
+ return m_orderedKeys->size();
+}
+
+IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry)
+ : m_entry(&entry)
+{
+ ASSERT(m_entry->m_key);
+}
+
+IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry, std::set<IDBKeyData>::iterator iterator)
+ : m_entry(&entry)
+ , m_forwardIterator(iterator)
+{
+}
+
+IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry, std::set<IDBKeyData>::reverse_iterator iterator)
+ : m_entry(&entry)
+ , m_forward(false)
+ , m_reverseIterator(iterator)
+{
+}
+
+const IDBKeyData& IndexValueEntry::Iterator::key() const
+{
+ ASSERT(isValid());
+ if (m_entry->unique()) {
+ ASSERT(m_entry->m_key);
+ return *m_entry->m_key;
+ }
+
+ return m_forward ? *m_forwardIterator : *m_reverseIterator;
+}
+
+bool IndexValueEntry::Iterator::isValid() const
+{
+#if !LOG_DISABLED
+ if (m_entry) {
+ if (m_entry->m_unique)
+ ASSERT(m_entry->m_key);
+ else
+ ASSERT(m_entry->m_orderedKeys);
+ }
+#endif
+
+ return m_entry;
+}
+
+void IndexValueEntry::Iterator::invalidate()
+{
+ m_entry = nullptr;
+}
+
+IndexValueEntry::Iterator& IndexValueEntry::Iterator::operator++()
+{
+ if (!isValid())
+ return *this;
+
+ if (m_entry->m_unique) {
+ invalidate();
+ return *this;
+ }
+
+ if (m_forward) {
+ ++m_forwardIterator;
+ if (m_forwardIterator == m_entry->m_orderedKeys->end())
+ invalidate();
+ } else {
+ ++m_reverseIterator;
+ if (m_reverseIterator == m_entry->m_orderedKeys->rend())
+ invalidate();
+ }
+
+ return *this;
+}
+
+IndexValueEntry::Iterator IndexValueEntry::begin()
+{
+ if (m_unique) {
+ ASSERT(m_key);
+ return { *this };
+ }
+
+ ASSERT(m_orderedKeys);
+ return { *this, m_orderedKeys->begin() };
+}
+
+IndexValueEntry::Iterator IndexValueEntry::reverseBegin(CursorDuplicity duplicity)
+{
+ if (m_unique) {
+ ASSERT(m_key);
+ return { *this };
+ }
+
+ ASSERT(m_orderedKeys);
+
+ if (duplicity == CursorDuplicity::Duplicates)
+ return { *this, m_orderedKeys->rbegin() };
+
+ auto iterator = m_orderedKeys->rend();
+ --iterator;
+ return { *this, iterator };
+}
+
+IndexValueEntry::Iterator IndexValueEntry::find(const IDBKeyData& key)
+{
+ if (m_unique) {
+ ASSERT(m_key);
+ return *m_key == key ? IndexValueEntry::Iterator(*this) : IndexValueEntry::Iterator();
+ }
+
+ ASSERT(m_orderedKeys);
+ auto iterator = m_orderedKeys->lower_bound(key);
+ if (iterator == m_orderedKeys->end())
+ return { };
+
+ return { *this, iterator };
+}
+
+IndexValueEntry::Iterator IndexValueEntry::reverseFind(const IDBKeyData& key, CursorDuplicity)
+{
+ if (m_unique) {
+ ASSERT(m_key);
+ return *m_key == key ? IndexValueEntry::Iterator(*this) : IndexValueEntry::Iterator();
+ }
+
+ ASSERT(m_orderedKeys);
+ auto iterator = std::set<IDBKeyData>::reverse_iterator(m_orderedKeys->upper_bound(key));
+ if (iterator == m_orderedKeys->rend())
+ return { };
+
+ return { *this, iterator };
+}
+
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h
new file mode 100644
index 000000000..df0543fcf
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyData.h"
+#include <set>
+
+namespace WebCore {
+
+class ThreadSafeDataBuffer;
+
+enum class CursorDuplicity;
+
+namespace IDBServer {
+
+class IndexValueEntry {
+public:
+ IndexValueEntry(bool unique);
+ ~IndexValueEntry();
+
+ void addKey(const IDBKeyData&);
+
+ // Returns true if a key was actually removed.
+ bool removeKey(const IDBKeyData&);
+
+ const IDBKeyData* getLowest() const;
+
+ uint64_t getCount() const;
+
+ class Iterator {
+ public:
+ Iterator()
+ {
+ }
+
+ Iterator(IndexValueEntry&);
+ Iterator(IndexValueEntry&, std::set<IDBKeyData>::iterator);
+ Iterator(IndexValueEntry&, std::set<IDBKeyData>::reverse_iterator);
+
+ bool isValid() const;
+ void invalidate();
+
+ const IDBKeyData& key() const;
+ const ThreadSafeDataBuffer& value() const;
+
+ Iterator& operator++();
+
+ private:
+ IndexValueEntry* m_entry { nullptr };
+ bool m_forward { true };
+ std::set<IDBKeyData>::iterator m_forwardIterator;
+ std::set<IDBKeyData>::reverse_iterator m_reverseIterator;
+ };
+
+ Iterator begin();
+ Iterator reverseBegin(CursorDuplicity);
+
+ // Finds the key, or the next higher record after the key.
+ Iterator find(const IDBKeyData&);
+ // Finds the key, or the next lowest record before the key.
+ Iterator reverseFind(const IDBKeyData&, CursorDuplicity);
+
+ bool unique() const { return m_unique; }
+
+private:
+ union {
+ std::set<IDBKeyData>* m_orderedKeys;
+ IDBKeyData* m_key;
+ };
+
+ bool m_unique;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp
new file mode 100644
index 000000000..339888462
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "IndexValueStore.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBError.h"
+#include "IDBKeyRangeData.h"
+#include "Logging.h"
+#include "MemoryIndex.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+IndexValueStore::IndexValueStore(bool unique)
+ : m_unique(unique)
+{
+}
+
+const IDBKeyData* IndexValueStore::lowestValueForKey(const IDBKeyData& key) const
+{
+ const auto& entry = m_records.get(key);
+ if (!entry)
+ return nullptr;
+
+ return entry->getLowest();
+}
+
+Vector<IDBKeyData> IndexValueStore::allValuesForKey(const IDBKeyData& key, uint32_t limit) const
+{
+ const auto& entry = m_records.get(key);
+ if (!entry)
+ return { };
+
+ Vector<IDBKeyData> results;
+ for (auto iterator = entry->begin(); results.size() < limit && iterator.isValid(); ++iterator)
+ results.append(iterator.key());
+
+ return results;
+}
+
+uint64_t IndexValueStore::countForKey(const IDBKeyData& key) const
+{
+ const auto& entry = m_records.get(key);
+ if (!entry)
+ return 0;
+
+ return entry->getCount();
+}
+
+bool IndexValueStore::contains(const IDBKeyData& key) const
+{
+ const auto& entry = m_records.get(key);
+ if (!entry)
+ return false;
+
+ ASSERT(entry->getCount());
+
+ return true;
+}
+
+IDBError IndexValueStore::addRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey)
+{
+ auto result = m_records.add(indexKey, nullptr);
+
+ if (!result.isNewEntry && m_unique)
+ return IDBError(IDBDatabaseException::ConstraintError);
+
+ if (result.isNewEntry)
+ result.iterator->value = std::make_unique<IndexValueEntry>(m_unique);
+
+ result.iterator->value->addKey(valueKey);
+ m_orderedKeys.insert(indexKey);
+
+ return { };
+}
+
+void IndexValueStore::removeRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey)
+{
+ auto iterator = m_records.find(indexKey);
+ if (!iterator->value)
+ return;
+
+ if (iterator->value->removeKey(valueKey))
+ m_records.remove(iterator);
+}
+
+void IndexValueStore::removeEntriesWithValueKey(MemoryIndex& index, const IDBKeyData& valueKey)
+{
+ Vector<IDBKeyData> entryKeysToRemove;
+ entryKeysToRemove.reserveInitialCapacity(m_records.size());
+
+ for (auto& entry : m_records) {
+ if (entry.value->removeKey(valueKey))
+ index.notifyCursorsOfValueChange(entry.key, valueKey);
+ if (!entry.value->getCount())
+ entryKeysToRemove.uncheckedAppend(entry.key);
+ }
+
+ for (auto& entry : entryKeysToRemove) {
+ m_orderedKeys.erase(entry);
+ m_records.remove(entry);
+ }
+}
+
+IDBKeyData IndexValueStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& range) const
+{
+ LOG(IndexedDB, "IndexValueStore::lowestKeyWithRecordInRange - %s", range.loggingString().utf8().data());
+
+ if (range.isExactlyOneKey())
+ return m_records.contains(range.lowerKey) ? range.lowerKey : IDBKeyData();
+
+ auto iterator = lowestIteratorInRange(range);
+ if (iterator == m_orderedKeys.end())
+ return { };
+
+ return *iterator;
+}
+
+std::set<IDBKeyData>::iterator IndexValueStore::lowestIteratorInRange(const IDBKeyRangeData& range) const
+{
+ auto lowestInRange = m_orderedKeys.lower_bound(range.lowerKey);
+
+ if (lowestInRange == m_orderedKeys.end())
+ return lowestInRange;
+
+ if (range.lowerOpen && *lowestInRange == range.lowerKey) {
+ ++lowestInRange;
+
+ if (lowestInRange == m_orderedKeys.end())
+ return lowestInRange;
+ }
+
+ if (!range.upperKey.isNull()) {
+ if (lowestInRange->compare(range.upperKey) > 0)
+ return m_orderedKeys.end();
+ if (range.upperOpen && *lowestInRange == range.upperKey)
+ return m_orderedKeys.end();
+ }
+
+ return lowestInRange;
+}
+
+std::set<IDBKeyData>::reverse_iterator IndexValueStore::highestReverseIteratorInRange(const IDBKeyRangeData& range) const
+{
+ auto highestInRange = std::set<IDBKeyData>::reverse_iterator(m_orderedKeys.upper_bound(range.upperKey));
+
+ if (highestInRange == m_orderedKeys.rend())
+ return highestInRange;
+
+ if (range.upperOpen && *highestInRange == range.upperKey) {
+ ++highestInRange;
+
+ if (highestInRange == m_orderedKeys.rend())
+ return highestInRange;
+ }
+
+ if (!range.lowerKey.isNull()) {
+ if (highestInRange->compare(range.lowerKey) < 0)
+ return m_orderedKeys.rend();
+ if (range.lowerOpen && *highestInRange == range.lowerKey)
+ return m_orderedKeys.rend();
+ }
+
+ return highestInRange;
+}
+
+IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, bool open)
+{
+ IDBKeyRangeData range;
+ if (!key.isNull())
+ range.lowerKey = key;
+ else
+ range.lowerKey = IDBKeyData::minimum();
+ range.lowerOpen = open;
+
+ auto iterator = lowestIteratorInRange(range);
+ if (iterator == m_orderedKeys.end())
+ return { };
+
+ auto record = m_records.get(*iterator);
+ ASSERT(record);
+
+ auto primaryIterator = record->begin();
+ ASSERT(primaryIterator.isValid());
+ return { *this, iterator, primaryIterator };
+}
+
+IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, const IDBKeyData& primaryKey)
+{
+ ASSERT(!key.isNull());
+ ASSERT(!primaryKey.isNull());
+
+ IDBKeyRangeData range;
+ range.lowerKey = key;
+ range.lowerOpen = false;
+
+ auto iterator = lowestIteratorInRange(range);
+ if (iterator == m_orderedKeys.end())
+ return { };
+
+ auto record = m_records.get(*iterator);
+ ASSERT(record);
+
+ // If the main record iterator is not equal to the key we were looking for,
+ // we know the primary key record should be the first.
+ if (*iterator != key) {
+ auto primaryIterator = record->begin();
+ ASSERT(primaryIterator.isValid());
+
+ return { *this, iterator, primaryIterator };
+ }
+
+ auto primaryIterator = record->find(primaryKey);
+ if (primaryIterator.isValid())
+ return { *this, iterator, primaryIterator };
+
+ // If we didn't find a primary key iterator in this entry,
+ // we need to move on to start of the next record.
+ iterator++;
+ if (iterator == m_orderedKeys.end())
+ return { };
+
+ record = m_records.get(*iterator);
+ ASSERT(record);
+
+ primaryIterator = record->begin();
+ ASSERT(primaryIterator.isValid());
+
+ return { *this, iterator, primaryIterator };
+}
+
+IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, CursorDuplicity duplicity, bool open)
+{
+ IDBKeyRangeData range;
+ if (!key.isNull())
+ range.upperKey = key;
+ else
+ range.upperKey = IDBKeyData::maximum();
+ range.upperOpen = open;
+
+ auto iterator = highestReverseIteratorInRange(range);
+ if (iterator == m_orderedKeys.rend())
+ return { };
+
+ auto record = m_records.get(*iterator);
+ ASSERT(record);
+
+ auto primaryIterator = record->reverseBegin(duplicity);
+ ASSERT(primaryIterator.isValid());
+ return { *this, duplicity, iterator, primaryIterator };
+}
+
+IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, const IDBKeyData& primaryKey, CursorDuplicity duplicity)
+{
+ ASSERT(!key.isNull());
+ ASSERT(!primaryKey.isNull());
+
+ IDBKeyRangeData range;
+ range.upperKey = key;
+ range.upperOpen = false;
+
+ auto iterator = highestReverseIteratorInRange(range);
+ if (iterator == m_orderedKeys.rend())
+ return { };
+
+ auto record = m_records.get(*iterator);
+ ASSERT(record);
+
+ auto primaryIterator = record->reverseFind(primaryKey, duplicity);
+ if (primaryIterator.isValid())
+ return { *this, duplicity, iterator, primaryIterator };
+
+ // If we didn't find a primary key iterator in this entry,
+ // we need to move on to start of the next record.
+ iterator++;
+ if (iterator == m_orderedKeys.rend())
+ return { };
+
+ record = m_records.get(*iterator);
+ ASSERT(record);
+
+ primaryIterator = record->reverseBegin(duplicity);
+ ASSERT(primaryIterator.isValid());
+
+ return { *this, duplicity, iterator, primaryIterator };
+}
+
+
+IndexValueStore::Iterator::Iterator(IndexValueStore& store, std::set<IDBKeyData>::iterator iterator, IndexValueEntry::Iterator primaryIterator)
+ : m_store(&store)
+ , m_forwardIterator(iterator)
+ , m_primaryKeyIterator(primaryIterator)
+{
+}
+
+IndexValueStore::Iterator::Iterator(IndexValueStore& store, CursorDuplicity duplicity, std::set<IDBKeyData>::reverse_iterator iterator, IndexValueEntry::Iterator primaryIterator)
+ : m_store(&store)
+ , m_forward(false)
+ , m_duplicity(duplicity)
+ , m_reverseIterator(iterator)
+ , m_primaryKeyIterator(primaryIterator)
+{
+}
+
+IndexValueStore::Iterator& IndexValueStore::Iterator::nextIndexEntry()
+{
+ if (!m_store)
+ return *this;
+
+ if (m_forward) {
+ ++m_forwardIterator;
+ if (m_forwardIterator == m_store->m_orderedKeys.end()) {
+ invalidate();
+ return *this;
+ }
+
+ auto* entry = m_store->m_records.get(*m_forwardIterator);
+ ASSERT(entry);
+
+ m_primaryKeyIterator = entry->begin();
+ ASSERT(m_primaryKeyIterator.isValid());
+ } else {
+ ++m_reverseIterator;
+ if (m_reverseIterator == m_store->m_orderedKeys.rend()) {
+ invalidate();
+ return *this;
+ }
+
+ auto* entry = m_store->m_records.get(*m_reverseIterator);
+ ASSERT(entry);
+
+ m_primaryKeyIterator = entry->reverseBegin(m_duplicity);
+ ASSERT(m_primaryKeyIterator.isValid());
+ }
+
+ return *this;
+}
+
+IndexValueStore::Iterator& IndexValueStore::Iterator::operator++()
+{
+ if (!isValid())
+ return *this;
+
+ ++m_primaryKeyIterator;
+ if (m_primaryKeyIterator.isValid())
+ return *this;
+
+ // Ran out of primary key records, so move the main index iterator.
+ return nextIndexEntry();
+}
+
+void IndexValueStore::Iterator::invalidate()
+{
+ m_store = nullptr;
+ m_primaryKeyIterator.invalidate();
+}
+
+bool IndexValueStore::Iterator::isValid()
+{
+ return m_store && m_primaryKeyIterator.isValid();
+}
+
+const IDBKeyData& IndexValueStore::Iterator::key()
+{
+ ASSERT(isValid());
+ return m_forward ? *m_forwardIterator : *m_reverseIterator;
+}
+
+const IDBKeyData& IndexValueStore::Iterator::primaryKey()
+{
+ ASSERT(isValid());
+ return m_primaryKeyIterator.key();
+}
+
+#if !LOG_DISABLED
+String IndexValueStore::loggingString() const
+{
+ StringBuilder builder;
+ for (auto& key : m_orderedKeys) {
+ builder.appendLiteral("Key: ");
+ builder.append(key.loggingString());
+ builder.appendLiteral(" Entry has ");
+ builder.appendNumber(m_records.get(key)->getCount());
+ builder.appendLiteral(" entries");
+ }
+ return builder.toString();
+}
+#endif
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/IndexValueStore.h b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.h
new file mode 100644
index 000000000..3af7c1fc0
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/IndexValueStore.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IDBKeyData.h"
+#include "IndexValueEntry.h"
+#include <set>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class IDBError;
+
+struct IDBKeyRangeData;
+
+namespace IDBServer {
+
+class MemoryIndex;
+
+typedef HashMap<IDBKeyData, std::unique_ptr<IndexValueEntry>, IDBKeyDataHash, IDBKeyDataHashTraits> IndexKeyValueMap;
+
+class IndexValueStore {
+public:
+ IndexValueStore(bool unique);
+
+ const IDBKeyData* lowestValueForKey(const IDBKeyData&) const;
+ Vector<IDBKeyData> allValuesForKey(const IDBKeyData&, uint32_t limit) const;
+ uint64_t countForKey(const IDBKeyData&) const;
+ IDBKeyData lowestKeyWithRecordInRange(const IDBKeyRangeData&) const;
+ bool contains(const IDBKeyData&) const;
+
+ IDBError addRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey);
+ void removeRecord(const IDBKeyData& indexKey, const IDBKeyData& valueKey);
+
+ void removeEntriesWithValueKey(MemoryIndex&, const IDBKeyData& valueKey);
+
+ class Iterator {
+ friend class IndexValueStore;
+ public:
+ Iterator()
+ {
+ }
+
+ Iterator(IndexValueStore&, std::set<IDBKeyData>::iterator, IndexValueEntry::Iterator);
+ Iterator(IndexValueStore&, CursorDuplicity, std::set<IDBKeyData>::reverse_iterator, IndexValueEntry::Iterator);
+
+ void invalidate();
+ bool isValid();
+
+ const IDBKeyData& key();
+ const IDBKeyData& primaryKey();
+ const ThreadSafeDataBuffer& value();
+
+ Iterator& operator++();
+ Iterator& nextIndexEntry();
+
+ private:
+ IndexValueStore* m_store { nullptr };
+ bool m_forward { true };
+ CursorDuplicity m_duplicity { CursorDuplicity::Duplicates };
+ std::set<IDBKeyData>::iterator m_forwardIterator;
+ std::set<IDBKeyData>::reverse_iterator m_reverseIterator;
+
+ IndexValueEntry::Iterator m_primaryKeyIterator;
+ };
+
+ // Returns an iterator pointing to the first primaryKey record in the requested key, or the next key if it doesn't exist.
+ Iterator find(const IDBKeyData&, bool open = false);
+ Iterator reverseFind(const IDBKeyData&, CursorDuplicity, bool open = false);
+
+ // Returns an iterator pointing to the key/primaryKey record, or the next one after it if it doesn't exist.
+ Iterator find(const IDBKeyData&, const IDBKeyData& primaryKey);
+ Iterator reverseFind(const IDBKeyData&, const IDBKeyData& primaryKey, CursorDuplicity);
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+private:
+ std::set<IDBKeyData>::iterator lowestIteratorInRange(const IDBKeyRangeData&) const;
+ std::set<IDBKeyData>::reverse_iterator highestReverseIteratorInRange(const IDBKeyRangeData&) const;
+
+ IndexKeyValueMap m_records;
+ std::set<IDBKeyData> m_orderedKeys;
+
+ bool m_unique;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp
new file mode 100644
index 000000000..0e7bb6bda
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "MemoryBackingStoreTransaction.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyRangeData.h"
+#include "IDBValue.h"
+#include "IndexedDB.h"
+#include "Logging.h"
+#include "MemoryIDBBackingStore.h"
+#include "MemoryObjectStore.h"
+#include <wtf/SetForScope.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+std::unique_ptr<MemoryBackingStoreTransaction> MemoryBackingStoreTransaction::create(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info)
+{
+ return std::make_unique<MemoryBackingStoreTransaction>(backingStore, info);
+}
+
+MemoryBackingStoreTransaction::MemoryBackingStoreTransaction(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info)
+ : m_backingStore(backingStore)
+ , m_info(info)
+{
+ if (m_info.mode() == IDBTransactionMode::Versionchange) {
+ IDBDatabaseInfo info;
+ auto error = m_backingStore.getOrEstablishDatabaseInfo(info);
+ if (error.isNull())
+ m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(info);
+ }
+}
+
+MemoryBackingStoreTransaction::~MemoryBackingStoreTransaction()
+{
+ ASSERT(!m_inProgress);
+}
+
+void MemoryBackingStoreTransaction::addNewObjectStore(MemoryObjectStore& objectStore)
+{
+ LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewObjectStore()");
+
+ ASSERT(isVersionChange());
+ m_versionChangeAddedObjectStores.add(&objectStore);
+
+ addExistingObjectStore(objectStore);
+}
+
+void MemoryBackingStoreTransaction::addNewIndex(MemoryIndex& index)
+{
+ LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewIndex()");
+
+ ASSERT(isVersionChange());
+ m_versionChangeAddedIndexes.add(&index);
+
+ addExistingIndex(index);
+}
+
+void MemoryBackingStoreTransaction::addExistingIndex(MemoryIndex& index)
+{
+ LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingIndex");
+
+ ASSERT(isWriting());
+
+ ASSERT(!m_indexes.contains(&index));
+ m_indexes.add(&index);
+}
+
+void MemoryBackingStoreTransaction::indexDeleted(Ref<MemoryIndex>&& index)
+{
+ m_indexes.remove(&index.get());
+
+ // If this MemoryIndex belongs to an object store that will not get restored if this transaction aborts,
+ // then we can forget about it altogether.
+ auto& objectStore = index->objectStore();
+ if (auto deletedObjectStore = m_deletedObjectStores.get(objectStore.info().name())) {
+ if (deletedObjectStore != &objectStore)
+ return;
+ }
+
+ auto addResult = m_deletedIndexes.add(index->info().name(), nullptr);
+ if (addResult.isNewEntry)
+ addResult.iterator->value = WTFMove(index);
+}
+
+void MemoryBackingStoreTransaction::addExistingObjectStore(MemoryObjectStore& objectStore)
+{
+ LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingObjectStore");
+
+ ASSERT(isWriting());
+
+ ASSERT(!m_objectStores.contains(&objectStore));
+ m_objectStores.add(&objectStore);
+
+ objectStore.writeTransactionStarted(*this);
+
+ m_originalKeyGenerators.add(&objectStore, objectStore.currentKeyGeneratorValue());
+}
+
+void MemoryBackingStoreTransaction::objectStoreDeleted(Ref<MemoryObjectStore>&& objectStore)
+{
+ ASSERT(m_objectStores.contains(&objectStore.get()));
+ m_objectStores.remove(&objectStore.get());
+
+ objectStore->deleteAllIndexes(*this);
+
+ auto addResult = m_deletedObjectStores.add(objectStore->info().name(), nullptr);
+ if (addResult.isNewEntry)
+ addResult.iterator->value = WTFMove(objectStore);
+}
+
+void MemoryBackingStoreTransaction::objectStoreCleared(MemoryObjectStore& objectStore, std::unique_ptr<KeyValueMap>&& keyValueMap, std::unique_ptr<std::set<IDBKeyData>>&& orderedKeys)
+{
+ ASSERT(m_objectStores.contains(&objectStore));
+
+ auto addResult = m_clearedKeyValueMaps.add(&objectStore, nullptr);
+
+ // If this object store has already been cleared during this transaction, we shouldn't remember this clearing.
+ if (!addResult.isNewEntry)
+ return;
+
+ addResult.iterator->value = WTFMove(keyValueMap);
+
+ ASSERT(!m_clearedOrderedKeys.contains(&objectStore));
+ m_clearedOrderedKeys.add(&objectStore, WTFMove(orderedKeys));
+}
+
+void MemoryBackingStoreTransaction::objectStoreRenamed(MemoryObjectStore& objectStore, const String& oldName)
+{
+ ASSERT(m_objectStores.contains(&objectStore));
+ ASSERT(m_info.mode() == IDBTransactionMode::Versionchange);
+
+ // We only care about the first rename in a given transaction, because if the transaction is aborted we want
+ // to go back to the first 'oldName'
+ m_originalObjectStoreNames.add(&objectStore, oldName);
+}
+
+void MemoryBackingStoreTransaction::indexRenamed(MemoryIndex& index, const String& oldName)
+{
+ ASSERT(m_objectStores.contains(&index.objectStore()));
+ ASSERT(m_info.mode() == IDBTransactionMode::Versionchange);
+
+ // We only care about the first rename in a given transaction, because if the transaction is aborted we want
+ // to go back to the first 'oldName'
+ m_originalIndexNames.add(&index, oldName);
+}
+
+void MemoryBackingStoreTransaction::indexCleared(MemoryIndex& index, std::unique_ptr<IndexValueStore>&& valueStore)
+{
+ auto addResult = m_clearedIndexValueStores.add(&index, nullptr);
+
+ // If this index has already been cleared during this transaction, we shouldn't remember this clearing.
+ if (!addResult.isNewEntry)
+ return;
+
+ addResult.iterator->value = WTFMove(valueStore);
+}
+
+void MemoryBackingStoreTransaction::recordValueChanged(MemoryObjectStore& objectStore, const IDBKeyData& key, ThreadSafeDataBuffer* value)
+{
+ ASSERT(m_objectStores.contains(&objectStore));
+
+ if (m_isAborting)
+ return;
+
+ // If this object store had been cleared during the transaction, no point in recording this
+ // individual key/value change as its entire key/value map will be restored upon abort.
+ if (m_clearedKeyValueMaps.contains(&objectStore))
+ return;
+
+ auto originalAddResult = m_originalValues.add(&objectStore, nullptr);
+ if (originalAddResult.isNewEntry)
+ originalAddResult.iterator->value = std::make_unique<KeyValueMap>();
+
+ auto* map = originalAddResult.iterator->value.get();
+
+ auto addResult = map->add(key, ThreadSafeDataBuffer());
+ if (!addResult.isNewEntry)
+ return;
+
+ if (value)
+ addResult.iterator->value = *value;
+}
+
+void MemoryBackingStoreTransaction::abort()
+{
+ LOG(IndexedDB, "MemoryBackingStoreTransaction::abort()");
+
+ SetForScope<bool> change(m_isAborting, true);
+
+ for (auto iterator : m_originalIndexNames)
+ iterator.key->rename(iterator.value);
+ m_originalIndexNames.clear();
+
+ for (auto iterator : m_originalObjectStoreNames)
+ iterator.key->rename(iterator.value);
+ m_originalObjectStoreNames.clear();
+
+ for (auto objectStore : m_versionChangeAddedObjectStores)
+ m_backingStore.removeObjectStoreForVersionChangeAbort(*objectStore);
+ m_versionChangeAddedObjectStores.clear();
+
+ for (auto& objectStore : m_deletedObjectStores.values()) {
+ m_backingStore.restoreObjectStoreForVersionChangeAbort(*objectStore);
+ ASSERT(!m_objectStores.contains(objectStore.get()));
+ m_objectStores.add(objectStore);
+ }
+ m_deletedObjectStores.clear();
+
+ if (m_originalDatabaseInfo) {
+ ASSERT(m_info.mode() == IDBTransactionMode::Versionchange);
+ m_backingStore.setDatabaseInfo(*m_originalDatabaseInfo);
+ }
+
+ // Restore cleared index value stores before we re-insert values into object stores
+ // because inserting those values will regenerate the appropriate index values.
+ for (auto& iterator : m_clearedIndexValueStores)
+ iterator.key->replaceIndexValueStore(WTFMove(iterator.value));
+ m_clearedIndexValueStores.clear();
+
+ for (auto& objectStore : m_objectStores) {
+ ASSERT(m_originalKeyGenerators.contains(objectStore.get()));
+ objectStore->setKeyGeneratorValue(m_originalKeyGenerators.get(objectStore.get()));
+
+ auto clearedKeyValueMap = m_clearedKeyValueMaps.take(objectStore.get());
+ if (clearedKeyValueMap) {
+ ASSERT(m_clearedOrderedKeys.contains(objectStore.get()));
+ objectStore->replaceKeyValueStore(WTFMove(clearedKeyValueMap), m_clearedOrderedKeys.take(objectStore.get()));
+ }
+
+ auto keyValueMap = m_originalValues.take(objectStore.get());
+ if (!keyValueMap)
+ continue;
+
+ for (auto entry : *keyValueMap) {
+ objectStore->deleteRecord(entry.key);
+ objectStore->addRecord(*this, entry.key, { entry.value });
+ }
+ }
+
+ for (auto& index : m_deletedIndexes.values())
+ index->objectStore().maybeRestoreDeletedIndex(*index);
+ m_deletedIndexes.clear();
+
+ finish();
+}
+
+void MemoryBackingStoreTransaction::commit()
+{
+ LOG(IndexedDB, "MemoryBackingStoreTransaction::commit()");
+
+ finish();
+}
+
+void MemoryBackingStoreTransaction::finish()
+{
+ m_inProgress = false;
+
+ if (!isWriting())
+ return;
+
+ for (auto& objectStore : m_objectStores)
+ objectStore->writeTransactionFinished(*this);
+ for (auto& objectStore : m_deletedObjectStores.values())
+ objectStore->writeTransactionFinished(*this);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h
new file mode 100644
index 000000000..5ac9302cc
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBDatabaseInfo.h"
+#include "IDBKeyData.h"
+#include "IDBTransactionInfo.h"
+#include "IndexValueStore.h"
+#include "ThreadSafeDataBuffer.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+class MemoryIDBBackingStore;
+class MemoryIndex;
+class MemoryObjectStore;
+
+typedef HashMap<IDBKeyData, ThreadSafeDataBuffer, IDBKeyDataHash, IDBKeyDataHashTraits> KeyValueMap;
+
+class MemoryBackingStoreTransaction {
+public:
+ static std::unique_ptr<MemoryBackingStoreTransaction> create(MemoryIDBBackingStore&, const IDBTransactionInfo&);
+
+ MemoryBackingStoreTransaction(MemoryIDBBackingStore&, const IDBTransactionInfo&);
+ ~MemoryBackingStoreTransaction();
+
+ bool isVersionChange() const { return m_info.mode() == IDBTransactionMode::Versionchange; }
+ bool isWriting() const { return m_info.mode() != IDBTransactionMode::Readonly; }
+ bool isAborting() const { return m_isAborting; }
+
+ const IDBDatabaseInfo& originalDatabaseInfo() const;
+
+ void addNewObjectStore(MemoryObjectStore&);
+ void addExistingObjectStore(MemoryObjectStore&);
+
+ void recordValueChanged(MemoryObjectStore&, const IDBKeyData&, ThreadSafeDataBuffer*);
+ void objectStoreDeleted(Ref<MemoryObjectStore>&&);
+ void objectStoreCleared(MemoryObjectStore&, std::unique_ptr<KeyValueMap>&&, std::unique_ptr<std::set<IDBKeyData>>&&);
+ void objectStoreRenamed(MemoryObjectStore&, const String& oldName);
+ void indexRenamed(MemoryIndex&, const String& oldName);
+ void indexCleared(MemoryIndex&, std::unique_ptr<IndexValueStore>&&);
+
+ void addNewIndex(MemoryIndex&);
+ void addExistingIndex(MemoryIndex&);
+ void indexDeleted(Ref<MemoryIndex>&&);
+
+ void abort();
+ void commit();
+
+private:
+ void finish();
+
+ MemoryIDBBackingStore& m_backingStore;
+ IDBTransactionInfo m_info;
+
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
+
+ bool m_inProgress { true };
+ bool m_isAborting { false };
+
+ HashSet<RefPtr<MemoryObjectStore>> m_objectStores;
+ HashSet<RefPtr<MemoryObjectStore>> m_versionChangeAddedObjectStores;
+ HashSet<RefPtr<MemoryIndex>> m_indexes;
+ HashSet<RefPtr<MemoryIndex>> m_versionChangeAddedIndexes;
+
+ HashMap<MemoryObjectStore*, uint64_t> m_originalKeyGenerators;
+ HashMap<String, RefPtr<MemoryObjectStore>> m_deletedObjectStores;
+ HashMap<String, RefPtr<MemoryIndex>> m_deletedIndexes;
+ HashMap<MemoryObjectStore*, std::unique_ptr<KeyValueMap>> m_originalValues;
+ HashMap<MemoryObjectStore*, std::unique_ptr<KeyValueMap>> m_clearedKeyValueMaps;
+ HashMap<MemoryObjectStore*, std::unique_ptr<std::set<IDBKeyData>>> m_clearedOrderedKeys;
+ HashMap<MemoryObjectStore*, String> m_originalObjectStoreNames;
+ HashMap<MemoryIndex*, String> m_originalIndexNames;
+ HashMap<MemoryIndex*, std::unique_ptr<IndexValueStore>> m_clearedIndexValueStores;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp
new file mode 100644
index 000000000..67059f313
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "MemoryCursor.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBResourceIdentifier.h"
+#include "MemoryIDBBackingStore.h"
+#include <wtf/HashMap.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+static HashMap<IDBResourceIdentifier, MemoryCursor*>& cursorMap()
+{
+ static NeverDestroyed<HashMap<IDBResourceIdentifier, MemoryCursor*>> map;
+ return map;
+}
+
+MemoryCursor::MemoryCursor(const IDBCursorInfo& info)
+ : m_info(info)
+{
+ ASSERT(!cursorMap().contains(m_info.identifier()));
+ cursorMap().set(m_info.identifier(), this);
+}
+
+MemoryCursor::~MemoryCursor()
+{
+ ASSERT(cursorMap().contains(m_info.identifier()));
+ cursorMap().remove(m_info.identifier());
+}
+
+MemoryCursor* MemoryCursor::cursorForIdentifier(const IDBResourceIdentifier& identifier)
+{
+ return cursorMap().get(identifier);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryCursor.h b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.h
new file mode 100644
index 000000000..64ac3e82d
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryCursor.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+
+namespace WebCore {
+
+class IDBGetResult;
+class IDBKeyData;
+class IDBResourceIdentifier;
+
+namespace IDBServer {
+
+class MemoryCursor {
+public:
+ virtual ~MemoryCursor();
+
+ virtual void currentData(IDBGetResult&) = 0;
+ virtual void iterate(const IDBKeyData&, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult&) = 0;
+
+ static MemoryCursor* cursorForIdentifier(const IDBResourceIdentifier&);
+
+protected:
+ MemoryCursor(const IDBCursorInfo&);
+
+ IDBCursorInfo m_info;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp
new file mode 100644
index 000000000..e6330891f
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "MemoryIDBBackingStore.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IDBGetAllRecordsData.h"
+#include "IDBGetRecordData.h"
+#include "IDBGetResult.h"
+#include "IDBIndexInfo.h"
+#include "IDBIterateCursorData.h"
+#include "IDBKeyRangeData.h"
+#include "Logging.h"
+#include "MemoryIndexCursor.h"
+#include "MemoryObjectStore.h"
+#include "MemoryObjectStoreCursor.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+// The IndexedDB spec states the value you can get from the key generator is 2^53
+static uint64_t maxGeneratedKeyValue = 0x20000000000000;
+
+std::unique_ptr<MemoryIDBBackingStore> MemoryIDBBackingStore::create(const IDBDatabaseIdentifier& identifier)
+{
+ return std::make_unique<MemoryIDBBackingStore>(identifier);
+}
+
+MemoryIDBBackingStore::MemoryIDBBackingStore(const IDBDatabaseIdentifier& identifier)
+ : m_identifier(identifier)
+{
+}
+
+MemoryIDBBackingStore::~MemoryIDBBackingStore()
+{
+}
+
+IDBError MemoryIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info)
+{
+ if (!m_databaseInfo)
+ m_databaseInfo = std::make_unique<IDBDatabaseInfo>(m_identifier.databaseName(), 0);
+
+ info = *m_databaseInfo;
+ return { };
+}
+
+void MemoryIDBBackingStore::setDatabaseInfo(const IDBDatabaseInfo& info)
+{
+ // It is not valid to directly set database info on a backing store that hasn't already set its own database info.
+ ASSERT(m_databaseInfo);
+
+ m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
+}
+
+IDBError MemoryIDBBackingStore::beginTransaction(const IDBTransactionInfo& info)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::beginTransaction");
+
+ if (m_transactions.contains(info.identifier()))
+ return { IDBDatabaseException::InvalidStateError, "Backing store asked to create transaction it already has a record of" };
+
+ auto transaction = MemoryBackingStoreTransaction::create(*this, info);
+
+ // VersionChange transactions are scoped to "every object store".
+ if (transaction->isVersionChange()) {
+ for (auto& objectStore : m_objectStoresByIdentifier.values())
+ transaction->addExistingObjectStore(*objectStore);
+ } else if (transaction->isWriting()) {
+ for (auto& iterator : m_objectStoresByName) {
+ if (info.objectStores().contains(iterator.key))
+ transaction->addExistingObjectStore(*iterator.value);
+ }
+ }
+
+ m_transactions.set(info.identifier(), WTFMove(transaction));
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::abortTransaction(const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::abortTransaction - %s", transactionIdentifier.loggingString().utf8().data());
+
+ auto transaction = m_transactions.take(transactionIdentifier);
+ if (!transaction)
+ return { IDBDatabaseException::InvalidStateError, "Backing store asked to abort transaction it didn't have record of" };
+
+ transaction->abort();
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::commitTransaction(const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::commitTransaction - %s", transactionIdentifier.loggingString().utf8().data());
+
+ auto transaction = m_transactions.take(transactionIdentifier);
+ if (!transaction)
+ return { IDBDatabaseException::InvalidStateError, "Backing store asked to commit transaction it didn't have record of" };
+
+ transaction->commit();
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::createObjectStore - adding OS %s with ID %" PRIu64, info.name().utf8().data(), info.identifier());
+
+ ASSERT(m_databaseInfo);
+ if (m_databaseInfo->hasObjectStore(info.name()))
+ return { IDBDatabaseException::ConstraintError };
+
+ ASSERT(!m_objectStoresByIdentifier.contains(info.identifier()));
+ auto objectStore = MemoryObjectStore::create(info);
+
+ m_databaseInfo->addExistingObjectStore(info);
+
+ auto rawTransaction = m_transactions.get(transactionIdentifier);
+ ASSERT(rawTransaction);
+ ASSERT(rawTransaction->isVersionChange());
+
+ rawTransaction->addNewObjectStore(objectStore.get());
+ registerObjectStore(WTFMove(objectStore));
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::deleteObjectStore");
+
+ ASSERT(m_databaseInfo);
+ if (!m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier))
+ return { IDBDatabaseException::ConstraintError };
+
+ auto transaction = m_transactions.get(transactionIdentifier);
+ ASSERT(transaction);
+ ASSERT(transaction->isVersionChange());
+
+ auto objectStore = takeObjectStoreByIdentifier(objectStoreIdentifier);
+ ASSERT(objectStore);
+ if (!objectStore)
+ return { IDBDatabaseException::ConstraintError };
+
+ m_databaseInfo->deleteObjectStore(objectStore->info().name());
+ transaction->objectStoreDeleted(*objectStore);
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::renameObjectStore");
+
+ ASSERT(m_databaseInfo);
+ if (!m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier))
+ return { IDBDatabaseException::ConstraintError };
+
+ auto transaction = m_transactions.get(transactionIdentifier);
+ ASSERT(transaction);
+ ASSERT(transaction->isVersionChange());
+
+ auto objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ ASSERT(objectStore);
+ if (!objectStore)
+ return { IDBDatabaseException::ConstraintError };
+
+ String oldName = objectStore->info().name();
+ objectStore->rename(newName);
+ transaction->objectStoreRenamed(*objectStore, oldName);
+
+ m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName);
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::clearObjectStore");
+ ASSERT(objectStoreIdentifier);
+
+ ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier));
+
+#if !LOG_DISABLED
+ auto transaction = m_transactions.get(transactionIdentifier);
+ ASSERT(transaction->isWriting());
+#endif
+
+ auto objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ if (!objectStore)
+ return { IDBDatabaseException::ConstraintError };
+
+ objectStore->clear();
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::createIndex");
+
+ auto rawTransaction = m_transactions.get(transactionIdentifier);
+ ASSERT(rawTransaction);
+ ASSERT(rawTransaction->isVersionChange());
+
+ auto* objectStore = m_objectStoresByIdentifier.get(info.objectStoreIdentifier());
+ if (!objectStore)
+ return { IDBDatabaseException::ConstraintError };
+
+ return objectStore->createIndex(*rawTransaction, info);
+}
+
+IDBError MemoryIDBBackingStore::deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::deleteIndex");
+
+ auto rawTransaction = m_transactions.get(transactionIdentifier);
+ ASSERT(rawTransaction);
+ ASSERT(rawTransaction->isVersionChange());
+
+ auto* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ if (!objectStore)
+ return { IDBDatabaseException::ConstraintError };
+
+ return objectStore->deleteIndex(*rawTransaction, indexIdentifier);
+}
+
+IDBError MemoryIDBBackingStore::renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::renameIndex");
+
+ ASSERT(m_databaseInfo);
+ auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ if (!objectStoreInfo)
+ return { IDBDatabaseException::ConstraintError };
+
+ auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier);
+ if (!indexInfo)
+ return { IDBDatabaseException::ConstraintError };
+
+ auto transaction = m_transactions.get(transactionIdentifier);
+ ASSERT(transaction);
+ ASSERT(transaction->isVersionChange());
+
+ auto objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ ASSERT(objectStore);
+ if (!objectStore)
+ return { IDBDatabaseException::ConstraintError };
+
+ auto* index = objectStore->indexForIdentifier(indexIdentifier);
+ ASSERT(index);
+ if (!index)
+ return { IDBDatabaseException::ConstraintError };
+
+ String oldName = index->info().name();
+ objectStore->renameIndex(*index, newName);
+ transaction->indexRenamed(*index, oldName);
+
+ indexInfo->rename(newName);
+
+ return { };
+}
+
+void MemoryIDBBackingStore::removeObjectStoreForVersionChangeAbort(MemoryObjectStore& objectStore)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::removeObjectStoreForVersionChangeAbort");
+
+ if (!m_objectStoresByIdentifier.contains(objectStore.info().identifier()))
+ return;
+
+ ASSERT(m_objectStoresByIdentifier.get(objectStore.info().identifier()) == &objectStore);
+
+ unregisterObjectStore(objectStore);
+}
+
+void MemoryIDBBackingStore::restoreObjectStoreForVersionChangeAbort(Ref<MemoryObjectStore>&& objectStore)
+{
+ registerObjectStore(WTFMove(objectStore));
+}
+
+IDBError MemoryIDBBackingStore::keyExistsInObjectStore(const IDBResourceIdentifier&, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, bool& keyExists)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::keyExistsInObjectStore");
+
+ ASSERT(objectStoreIdentifier);
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ RELEASE_ASSERT(objectStore);
+
+ keyExists = objectStore->containsRecord(keyData);
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::deleteRange");
+
+ ASSERT(objectStoreIdentifier);
+
+ if (!m_transactions.contains(transactionIdentifier))
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to delete from") };
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") };
+
+ objectStore->deleteRange(range);
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IDBValue& value)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::addRecord");
+
+ ASSERT(objectStoreInfo.identifier());
+
+ auto transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to put record") };
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreInfo.identifier());
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found to put record") };
+
+ return objectStore->addRecord(*transaction, keyData, value);
+}
+
+IDBError MemoryIDBBackingStore::getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IDBGetRecordDataType type, IDBGetResult& outValue)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::getRecord");
+
+ ASSERT(objectStoreIdentifier);
+
+ if (!m_transactions.contains(transactionIdentifier))
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get record") };
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") };
+
+ switch (type) {
+ case IDBGetRecordDataType::KeyAndValue:
+ outValue = objectStore->valueForKeyRange(range);
+ break;
+ case IDBGetRecordDataType::KeyOnly:
+ outValue = objectStore->lowestKeyWithRecordInRange(range);
+ break;
+ }
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::getAllRecords");
+
+ ASSERT(getAllRecordsData.objectStoreIdentifier);
+
+ if (!m_transactions.contains(transactionIdentifier))
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get all records") };
+
+ auto* objectStore = m_objectStoresByIdentifier.get(getAllRecordsData.objectStoreIdentifier);
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") };
+
+ if (getAllRecordsData.indexIdentifier) {
+ auto* index = objectStore->indexForIdentifier(getAllRecordsData.indexIdentifier);
+ if (!index)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store index found") };
+
+ index->getAllRecords(getAllRecordsData.keyRangeData, getAllRecordsData.count, getAllRecordsData.getAllType, result);
+ } else
+ objectStore->getAllRecords(getAllRecordsData.keyRangeData, getAllRecordsData.count, getAllRecordsData.getAllType, result);
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range, IDBGetResult& outValue)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::getIndexRecord");
+
+ ASSERT(objectStoreIdentifier);
+
+ if (!m_transactions.contains(transactionIdentifier))
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get record") };
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") };
+
+ outValue = objectStore->indexValueForKeyRange(indexIdentifier, recordType, range);
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, uint64_t& outCount)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::getCount");
+
+ ASSERT(objectStoreIdentifier);
+
+ if (!m_transactions.contains(transactionIdentifier))
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found to get count") };
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") };
+
+ outCount = objectStore->countForKeyRange(indexIdentifier, range);
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::generateKeyNumber");
+ ASSERT(objectStoreIdentifier);
+ ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier));
+ ASSERT_UNUSED(transactionIdentifier, m_transactions.get(transactionIdentifier)->isWriting());
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ RELEASE_ASSERT(objectStore);
+
+ keyNumber = objectStore->currentKeyGeneratorValue();
+ if (keyNumber > maxGeneratedKeyValue)
+ return { IDBDatabaseException::ConstraintError, "Cannot generate new key value over 2^53 for object store operation" };
+
+ objectStore->setKeyGeneratorValue(keyNumber + 1);
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::revertGeneratedKeyNumber");
+ ASSERT(objectStoreIdentifier);
+ ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier));
+ ASSERT_UNUSED(transactionIdentifier, m_transactions.get(transactionIdentifier)->isWriting());
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ RELEASE_ASSERT(objectStore);
+
+ objectStore->setKeyGeneratorValue(keyNumber);
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::maybeUpdateKeyGeneratorNumber");
+ ASSERT(objectStoreIdentifier);
+ ASSERT_UNUSED(transactionIdentifier, m_transactions.contains(transactionIdentifier));
+ ASSERT_UNUSED(transactionIdentifier, m_transactions.get(transactionIdentifier)->isWriting());
+
+ MemoryObjectStore* objectStore = m_objectStoresByIdentifier.get(objectStoreIdentifier);
+ RELEASE_ASSERT(objectStore);
+
+ if (newKeyNumber < objectStore->currentKeyGeneratorValue())
+ return { };
+
+ uint64_t newKeyInteger(newKeyNumber);
+ if (newKeyInteger <= uint64_t(newKeyNumber))
+ ++newKeyInteger;
+
+ ASSERT(newKeyInteger > uint64_t(newKeyNumber));
+
+ objectStore->setKeyGeneratorValue(newKeyInteger);
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info, IDBGetResult& outData)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::openCursor");
+
+ ASSERT(!MemoryCursor::cursorForIdentifier(info.identifier()));
+
+ if (!m_transactions.contains(transactionIdentifier))
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found in which to open a cursor") };
+
+ switch (info.cursorSource()) {
+ case IndexedDB::CursorSource::ObjectStore: {
+ auto* objectStore = m_objectStoresByIdentifier.get(info.sourceIdentifier());
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") };
+
+ MemoryCursor* cursor = objectStore->maybeOpenCursor(info);
+ if (!cursor)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not create object store cursor in backing store") };
+
+ cursor->currentData(outData);
+ break;
+ }
+ case IndexedDB::CursorSource::Index:
+ auto* objectStore = m_objectStoresByIdentifier.get(info.objectStoreIdentifier());
+ if (!objectStore)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store object store found") };
+
+ auto* index = objectStore->indexForIdentifier(info.sourceIdentifier());
+ if (!index)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store index found") };
+
+ MemoryCursor* cursor = index->maybeOpenCursor(info);
+ if (!cursor)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not create index cursor in backing store") };
+
+ cursor->currentData(outData);
+ break;
+ }
+
+ return { };
+}
+
+IDBError MemoryIDBBackingStore::iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data, IDBGetResult& outData)
+{
+ LOG(IndexedDB, "MemoryIDBBackingStore::iterateCursor");
+
+ if (!m_transactions.contains(transactionIdentifier))
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store transaction found in which to iterate cursor") };
+
+ auto* cursor = MemoryCursor::cursorForIdentifier(cursorIdentifier);
+ if (!cursor)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No backing store cursor found in which to iterate cursor") };
+
+ cursor->iterate(data.keyData, data.primaryKeyData, data.count, outData);
+
+ return { };
+}
+
+void MemoryIDBBackingStore::registerObjectStore(Ref<MemoryObjectStore>&& objectStore)
+{
+ ASSERT(!m_objectStoresByIdentifier.contains(objectStore->info().identifier()));
+ ASSERT(!m_objectStoresByName.contains(objectStore->info().name()));
+
+ m_objectStoresByName.set(objectStore->info().name(), &objectStore.get());
+ m_objectStoresByIdentifier.set(objectStore->info().identifier(), WTFMove(objectStore));
+}
+
+void MemoryIDBBackingStore::unregisterObjectStore(MemoryObjectStore& objectStore)
+{
+ ASSERT(m_objectStoresByIdentifier.contains(objectStore.info().identifier()));
+ ASSERT(m_objectStoresByName.contains(objectStore.info().name()));
+
+ m_objectStoresByName.remove(objectStore.info().name());
+ m_objectStoresByIdentifier.remove(objectStore.info().identifier());
+}
+
+RefPtr<MemoryObjectStore> MemoryIDBBackingStore::takeObjectStoreByIdentifier(uint64_t identifier)
+{
+ auto objectStoreByIdentifier = m_objectStoresByIdentifier.take(identifier);
+ if (!objectStoreByIdentifier)
+ return nullptr;
+
+ auto objectStore = m_objectStoresByName.take(objectStoreByIdentifier->info().name());
+ ASSERT_UNUSED(objectStore, objectStore);
+
+ return objectStoreByIdentifier;
+}
+
+IDBObjectStoreInfo* MemoryIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier)
+{
+ ASSERT(m_databaseInfo);
+ return m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+}
+
+void MemoryIDBBackingStore::deleteBackingStore()
+{
+ // The in-memory IDB backing store doesn't need to do any cleanup when it is deleted.
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h
new file mode 100644
index 000000000..e93a17df3
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBBackingStore.h"
+#include "IDBDatabaseIdentifier.h"
+#include "IDBResourceIdentifier.h"
+#include "MemoryBackingStoreTransaction.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+class MemoryObjectStore;
+
+class MemoryIDBBackingStore : public IDBBackingStore {
+public:
+ static std::unique_ptr<MemoryIDBBackingStore> create(const IDBDatabaseIdentifier&);
+
+ MemoryIDBBackingStore(const IDBDatabaseIdentifier&);
+ ~MemoryIDBBackingStore() final;
+
+ IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) final;
+ void setDatabaseInfo(const IDBDatabaseInfo&);
+
+ IDBError beginTransaction(const IDBTransactionInfo&) final;
+ IDBError abortTransaction(const IDBResourceIdentifier& transactionIdentifier) final;
+ IDBError commitTransaction(const IDBResourceIdentifier& transactionIdentifier) final;
+ IDBError createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&) final;
+ IDBError deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final;
+ IDBError renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) final;
+ IDBError clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final;
+ IDBError createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&) final;
+ IDBError deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) final;
+ IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final;
+ IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) final;
+ IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) final;
+ IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) final;
+ IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) final;
+ IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) final;
+ IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) final;
+ IDBError getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, uint64_t& outCount) final;
+ IDBError generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber) final;
+ IDBError revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber) final;
+ IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) final;
+ IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) final;
+ IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) final;
+ bool prefetchCursor(const IDBResourceIdentifier&, const IDBResourceIdentifier&) final { return false; }
+
+ IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final;
+ void deleteBackingStore() final;
+
+ bool supportsSimultaneousTransactions() final { return true; }
+ bool isEphemeral() final { return true; }
+
+ void removeObjectStoreForVersionChangeAbort(MemoryObjectStore&);
+ void restoreObjectStoreForVersionChangeAbort(Ref<MemoryObjectStore>&&);
+
+private:
+ RefPtr<MemoryObjectStore> takeObjectStoreByIdentifier(uint64_t identifier);
+
+ IDBDatabaseIdentifier m_identifier;
+ std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
+
+ HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryBackingStoreTransaction>> m_transactions;
+
+ void registerObjectStore(Ref<MemoryObjectStore>&&);
+ void unregisterObjectStore(MemoryObjectStore&);
+ HashMap<uint64_t, RefPtr<MemoryObjectStore>> m_objectStoresByIdentifier;
+ HashMap<String, MemoryObjectStore*> m_objectStoresByName;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp
new file mode 100644
index 000000000..e92d26801
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "MemoryIndex.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBError.h"
+#include "IDBGetAllResult.h"
+#include "IDBGetResult.h"
+#include "IDBKeyRangeData.h"
+#include "IndexKey.h"
+#include "Logging.h"
+#include "MemoryBackingStoreTransaction.h"
+#include "MemoryIndexCursor.h"
+#include "MemoryObjectStore.h"
+#include "ThreadSafeDataBuffer.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<MemoryIndex> MemoryIndex::create(const IDBIndexInfo& info, MemoryObjectStore& objectStore)
+{
+ return adoptRef(*new MemoryIndex(info, objectStore));
+}
+
+MemoryIndex::MemoryIndex(const IDBIndexInfo& info, MemoryObjectStore& objectStore)
+ : m_info(info)
+ , m_objectStore(objectStore)
+{
+}
+
+MemoryIndex::~MemoryIndex()
+{
+}
+
+void MemoryIndex::cursorDidBecomeClean(MemoryIndexCursor& cursor)
+{
+ m_cleanCursors.add(&cursor);
+}
+
+void MemoryIndex::cursorDidBecomeDirty(MemoryIndexCursor& cursor)
+{
+ m_cleanCursors.remove(&cursor);
+}
+
+void MemoryIndex::objectStoreCleared()
+{
+ auto transaction = m_objectStore.writeTransaction();
+ ASSERT(transaction);
+
+ transaction->indexCleared(*this, WTFMove(m_records));
+
+ notifyCursorsOfAllRecordsChanged();
+}
+
+void MemoryIndex::notifyCursorsOfValueChange(const IDBKeyData& indexKey, const IDBKeyData& primaryKey)
+{
+ Vector<MemoryIndexCursor*> cursors;
+ copyToVector(m_cleanCursors, cursors);
+ for (auto* cursor : cursors)
+ cursor->indexValueChanged(indexKey, primaryKey);
+}
+
+void MemoryIndex::notifyCursorsOfAllRecordsChanged()
+{
+ Vector<MemoryIndexCursor*> cursors;
+ copyToVector(m_cleanCursors, cursors);
+ for (auto* cursor : cursors)
+ cursor->indexRecordsAllChanged();
+
+ ASSERT(m_cleanCursors.isEmpty());
+}
+
+void MemoryIndex::clearIndexValueStore()
+{
+ ASSERT(m_objectStore.writeTransaction());
+ ASSERT(m_objectStore.writeTransaction()->isAborting());
+
+ m_records = nullptr;
+}
+
+void MemoryIndex::replaceIndexValueStore(std::unique_ptr<IndexValueStore>&& valueStore)
+{
+ ASSERT(m_objectStore.writeTransaction());
+ ASSERT(m_objectStore.writeTransaction()->isAborting());
+
+ m_records = WTFMove(valueStore);
+}
+
+IDBGetResult MemoryIndex::getResultForKeyRange(IndexedDB::IndexRecordType type, const IDBKeyRangeData& range) const
+{
+ LOG(IndexedDB, "MemoryIndex::getResultForKeyRange - %s", range.loggingString().utf8().data());
+
+ if (!m_records)
+ return { };
+
+ IDBKeyData keyToLookFor;
+ if (range.isExactlyOneKey())
+ keyToLookFor = range.lowerKey;
+ else
+ keyToLookFor = m_records->lowestKeyWithRecordInRange(range);
+
+ if (keyToLookFor.isNull())
+ return { };
+
+ const IDBKeyData* keyValue = m_records->lowestValueForKey(keyToLookFor);
+
+ if (!keyValue)
+ return { };
+
+ return type == IndexedDB::IndexRecordType::Key ? IDBGetResult(*keyValue) : IDBGetResult(m_objectStore.valueForKeyRange(*keyValue));
+}
+
+uint64_t MemoryIndex::countForKeyRange(const IDBKeyRangeData& inRange)
+{
+ LOG(IndexedDB, "MemoryIndex::countForKeyRange");
+
+ if (!m_records)
+ return 0;
+
+ uint64_t count = 0;
+ IDBKeyRangeData range = inRange;
+ while (true) {
+ auto key = m_records->lowestKeyWithRecordInRange(range);
+ if (key.isNull())
+ break;
+
+ count += m_records->countForKey(key);
+
+ range.lowerKey = key;
+ range.lowerOpen = true;
+ }
+
+ return count;
+}
+
+void MemoryIndex::getAllRecords(const IDBKeyRangeData& keyRangeData, std::optional<uint32_t> count, IndexedDB::GetAllType type, IDBGetAllResult& result) const
+{
+ LOG(IndexedDB, "MemoryIndex::getAllRecords");
+
+ result = { type };
+
+ if (!m_records)
+ return;
+
+ uint32_t targetCount;
+ if (count && count.value())
+ targetCount = count.value();
+ else
+ targetCount = std::numeric_limits<uint32_t>::max();
+
+ IDBKeyRangeData range = keyRangeData;
+ uint32_t currentCount = 0;
+ while (currentCount < targetCount) {
+ auto key = m_records->lowestKeyWithRecordInRange(range);
+ if (key.isNull())
+ return;
+
+ range.lowerKey = key;
+ range.lowerOpen = true;
+
+ auto allValues = m_records->allValuesForKey(key, targetCount - currentCount);
+ for (auto& keyValue : allValues) {
+ if (type == IndexedDB::GetAllType::Keys) {
+ IDBKeyData keyCopy { keyValue };
+ result.addKey(WTFMove(keyCopy));
+ } else
+ result.addValue(m_objectStore.valueForKeyRange(keyValue));
+ }
+
+ currentCount += allValues.size();
+ }
+}
+
+
+IDBError MemoryIndex::putIndexKey(const IDBKeyData& valueKey, const IndexKey& indexKey)
+{
+ LOG(IndexedDB, "MemoryIndex::provisionalPutIndexKey");
+
+ if (!m_records) {
+ m_records = std::make_unique<IndexValueStore>(m_info.unique());
+ notifyCursorsOfAllRecordsChanged();
+ }
+
+ if (!m_info.multiEntry()) {
+ IDBKeyData key = indexKey.asOneKey();
+ IDBError result = m_records->addRecord(key, valueKey);
+ notifyCursorsOfValueChange(key, valueKey);
+ return result;
+ }
+
+ Vector<IDBKeyData> keys = indexKey.multiEntry();
+
+ if (m_info.unique()) {
+ for (auto& key : keys) {
+ if (m_records->contains(key))
+ return IDBError(IDBDatabaseException::ConstraintError);
+ }
+ }
+
+ for (auto& key : keys) {
+ auto error = m_records->addRecord(key, valueKey);
+ ASSERT_UNUSED(error, error.isNull());
+ notifyCursorsOfValueChange(key, valueKey);
+ }
+
+ return { };
+}
+
+void MemoryIndex::removeRecord(const IDBKeyData& valueKey, const IndexKey& indexKey)
+{
+ LOG(IndexedDB, "MemoryIndex::removeRecord");
+
+ ASSERT(m_records);
+
+ if (!m_info.multiEntry()) {
+ IDBKeyData key = indexKey.asOneKey();
+ m_records->removeRecord(key, valueKey);
+ notifyCursorsOfValueChange(key, valueKey);
+ return;
+ }
+
+ Vector<IDBKeyData> keys = indexKey.multiEntry();
+ for (auto& key : keys) {
+ m_records->removeRecord(key, valueKey);
+ notifyCursorsOfValueChange(key, valueKey);
+ }
+}
+
+void MemoryIndex::removeEntriesWithValueKey(const IDBKeyData& valueKey)
+{
+ LOG(IndexedDB, "MemoryIndex::removeEntriesWithValueKey");
+
+ if (!m_records)
+ return;
+
+ m_records->removeEntriesWithValueKey(*this, valueKey);
+}
+
+MemoryIndexCursor* MemoryIndex::maybeOpenCursor(const IDBCursorInfo& info)
+{
+ auto result = m_cursors.add(info.identifier(), nullptr);
+ if (!result.isNewEntry)
+ return nullptr;
+
+ result.iterator->value = std::make_unique<MemoryIndexCursor>(*this, info);
+ return result.iterator->value.get();
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndex.h b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.h
new file mode 100644
index 000000000..5bf9cba89
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndex.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBIndexInfo.h"
+#include "IDBResourceIdentifier.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBError;
+class IDBGetAllResult;
+class IDBGetResult;
+class IDBKeyData;
+class IndexKey;
+class ThreadSafeDataBuffer;
+
+struct IDBKeyRangeData;
+
+namespace IndexedDB {
+enum class GetAllType;
+enum class IndexRecordType;
+}
+
+namespace IDBServer {
+
+class IndexValueStore;
+class MemoryBackingStoreTransaction;
+class MemoryIndexCursor;
+class MemoryObjectStore;
+
+class MemoryIndex : public RefCounted<MemoryIndex> {
+public:
+ static Ref<MemoryIndex> create(const IDBIndexInfo&, MemoryObjectStore&);
+
+ ~MemoryIndex();
+
+ const IDBIndexInfo& info() const { return m_info; }
+
+ void rename(const String& newName) { m_info.rename(newName); }
+
+ IDBGetResult getResultForKeyRange(IndexedDB::IndexRecordType, const IDBKeyRangeData&) const;
+ uint64_t countForKeyRange(const IDBKeyRangeData&);
+ void getAllRecords(const IDBKeyRangeData&, std::optional<uint32_t> count, IndexedDB::GetAllType, IDBGetAllResult&) const;
+
+ IDBError putIndexKey(const IDBKeyData&, const IndexKey&);
+
+ void removeEntriesWithValueKey(const IDBKeyData&);
+ void removeRecord(const IDBKeyData&, const IndexKey&);
+
+ void objectStoreCleared();
+ void clearIndexValueStore();
+ void replaceIndexValueStore(std::unique_ptr<IndexValueStore>&&);
+
+ MemoryIndexCursor* maybeOpenCursor(const IDBCursorInfo&);
+
+ IndexValueStore* valueStore() { return m_records.get(); }
+
+ MemoryObjectStore& objectStore() { return m_objectStore; }
+
+ void cursorDidBecomeClean(MemoryIndexCursor&);
+ void cursorDidBecomeDirty(MemoryIndexCursor&);
+
+ void notifyCursorsOfValueChange(const IDBKeyData& indexKey, const IDBKeyData& primaryKey);
+
+private:
+ MemoryIndex(const IDBIndexInfo&, MemoryObjectStore&);
+
+ uint64_t recordCountForKey(const IDBKeyData&) const;
+
+ void notifyCursorsOfAllRecordsChanged();
+
+ IDBIndexInfo m_info;
+ MemoryObjectStore& m_objectStore;
+
+ std::unique_ptr<IndexValueStore> m_records;
+
+ HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryIndexCursor>> m_cursors;
+ HashSet<MemoryIndexCursor*> m_cleanCursors;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp
new file mode 100644
index 000000000..72b6eab56
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "MemoryIndexCursor.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IDBGetResult.h"
+#include "IndexValueStore.h"
+#include "Logging.h"
+#include "MemoryCursor.h"
+#include "MemoryIndex.h"
+#include "MemoryObjectStore.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+MemoryIndexCursor::MemoryIndexCursor(MemoryIndex& index, const IDBCursorInfo& info)
+ : MemoryCursor(info)
+ , m_index(index)
+{
+ LOG(IndexedDB, "MemoryIndexCursor::MemoryIndexCursor %s", info.range().loggingString().utf8().data());
+
+ auto* valueStore = m_index.valueStore();
+ if (!valueStore)
+ return;
+
+ if (m_info.isDirectionForward())
+ m_currentIterator = valueStore->find(m_info.range().lowerKey, m_info.range().lowerOpen);
+ else
+ m_currentIterator = valueStore->reverseFind(m_info.range().upperKey, m_info.duplicity(), m_info.range().upperOpen);
+
+ if (m_currentIterator.isValid() && m_info.range().containsKey(m_currentIterator.key())) {
+ m_currentKey = m_currentIterator.key();
+ m_currentPrimaryKey = m_currentIterator.primaryKey();
+ m_index.cursorDidBecomeClean(*this);
+ } else
+ m_currentIterator.invalidate();
+}
+
+MemoryIndexCursor::~MemoryIndexCursor()
+{
+}
+
+void MemoryIndexCursor::currentData(IDBGetResult& getResult)
+{
+ if (!m_currentIterator.isValid()) {
+ getResult = { };
+ return;
+ }
+
+ if (m_info.cursorType() == IndexedDB::CursorType::KeyOnly)
+ getResult = { m_currentKey, m_currentPrimaryKey };
+ else {
+ IDBValue value = { m_index.objectStore().valueForKey(m_currentPrimaryKey), { }, { } };
+ getResult = { m_currentKey, m_currentPrimaryKey, WTFMove(value) };
+ }
+}
+
+void MemoryIndexCursor::iterate(const IDBKeyData& key, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult& getResult)
+{
+ LOG(IndexedDB, "MemoryIndexCursor::iterate to key %s, %u count", key.loggingString().utf8().data(), count);
+
+#ifndef NDEBUG
+ if (primaryKey.isValid())
+ ASSERT(key.isValid());
+#endif
+
+ if (key.isValid()) {
+ // Cannot iterate by both a count and to a key
+ ASSERT(!count);
+
+ auto* valueStore = m_index.valueStore();
+ if (!valueStore) {
+ m_currentKey = { };
+ m_currentPrimaryKey = { };
+ getResult = { };
+ return;
+ }
+
+ if (primaryKey.isValid()) {
+ if (m_info.isDirectionForward())
+ m_currentIterator = valueStore->find(key, primaryKey);
+ else
+ m_currentIterator = valueStore->reverseFind(key, primaryKey, m_info.duplicity());
+ } else {
+ if (m_info.isDirectionForward())
+ m_currentIterator = valueStore->find(key);
+ else
+ m_currentIterator = valueStore->reverseFind(key, m_info.duplicity());
+ }
+
+ if (m_currentIterator.isValid() && !m_info.range().containsKey(m_currentIterator.key()))
+ m_currentIterator.invalidate();
+
+ if (!m_currentIterator.isValid()) {
+ m_currentKey = { };
+ m_currentPrimaryKey = { };
+ getResult = { };
+ return;
+ }
+
+ m_index.cursorDidBecomeClean(*this);
+
+ m_currentKey = m_currentIterator.key();
+ m_currentPrimaryKey = m_currentIterator.primaryKey();
+ currentData(getResult);
+
+ return;
+ }
+
+ // If there was not a valid key argument and no positive count argument
+ // that means the default iteration count of "1"
+ if (!count)
+ count = 1;
+
+ if (!m_currentIterator.isValid()) {
+ auto* valueStore = m_index.valueStore();
+ if (!valueStore) {
+ m_currentKey = { };
+ m_currentPrimaryKey = { };
+ getResult = { };
+ return;
+ }
+
+ switch (m_info.cursorDirection()) {
+ case IndexedDB::CursorDirection::Next:
+ m_currentIterator = valueStore->find(m_currentKey, m_currentPrimaryKey);
+ break;
+ case IndexedDB::CursorDirection::Nextunique:
+ m_currentIterator = valueStore->find(m_currentKey, true);
+ break;
+ case IndexedDB::CursorDirection::Prev:
+ m_currentIterator = valueStore->reverseFind(m_currentKey, m_currentPrimaryKey, m_info.duplicity());
+ break;
+ case IndexedDB::CursorDirection::Prevunique:
+ m_currentIterator = valueStore->reverseFind(m_currentKey, m_info.duplicity(), true);
+ break;
+ }
+
+ if (!m_currentIterator.isValid()) {
+ m_currentKey = { };
+ m_currentPrimaryKey = { };
+ getResult = { };
+ return;
+ }
+
+ m_index.cursorDidBecomeClean(*this);
+
+ // If we restored the current iterator and it does *not* match the current key/primaryKey,
+ // then it is the next record in line and we should consider that an iteration.
+ if (m_currentKey != m_currentIterator.key() || m_currentPrimaryKey != m_currentIterator.primaryKey())
+ --count;
+ }
+
+ ASSERT(m_currentIterator.isValid());
+
+ while (count) {
+ if (m_info.duplicity() == CursorDuplicity::NoDuplicates)
+ m_currentIterator.nextIndexEntry();
+ else
+ ++m_currentIterator;
+
+ if (!m_currentIterator.isValid())
+ break;
+
+ --count;
+ }
+
+ if (m_currentIterator.isValid() && !m_info.range().containsKey(m_currentIterator.key()))
+ m_currentIterator.invalidate();
+
+ // Not having a valid iterator after finishing any iteration means we've reached the end of the cursor.
+ if (!m_currentIterator.isValid()) {
+ m_currentKey = { };
+ m_currentPrimaryKey = { };
+ getResult = { };
+ return;
+ }
+
+ m_currentKey = m_currentIterator.key();
+ m_currentPrimaryKey = m_currentIterator.primaryKey();
+ currentData(getResult);
+}
+
+void MemoryIndexCursor::indexRecordsAllChanged()
+{
+ m_currentIterator.invalidate();
+ m_index.cursorDidBecomeDirty(*this);
+}
+
+void MemoryIndexCursor::indexValueChanged(const IDBKeyData& key, const IDBKeyData& primaryKey)
+{
+ if (m_currentKey != key || m_currentPrimaryKey != primaryKey)
+ return;
+
+ m_currentIterator.invalidate();
+ m_index.cursorDidBecomeDirty(*this);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h
new file mode 100644
index 000000000..d55d20709
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IndexValueStore.h"
+#include "MemoryCursor.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+class MemoryIndex;
+
+class MemoryIndexCursor : public MemoryCursor {
+public:
+ MemoryIndexCursor(MemoryIndex&, const IDBCursorInfo&);
+ virtual ~MemoryIndexCursor();
+
+ void indexRecordsAllChanged();
+ void indexValueChanged(const IDBKeyData& indexKey, const IDBKeyData& primaryKey);
+
+private:
+ void currentData(IDBGetResult&) final;
+ void iterate(const IDBKeyData&, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult&) final;
+
+ MemoryIndex& m_index;
+
+ IndexValueStore::Iterator m_currentIterator;
+ IDBKeyData m_currentKey;
+ IDBKeyData m_currentPrimaryKey;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp
new file mode 100644
index 000000000..34b4e129a
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "MemoryObjectStore.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBBindingUtilities.h"
+#include "IDBDatabaseException.h"
+#include "IDBError.h"
+#include "IDBGetAllResult.h"
+#include "IDBKeyRangeData.h"
+#include "IDBValue.h"
+#include "IndexKey.h"
+#include "Logging.h"
+#include "MemoryBackingStoreTransaction.h"
+#include "UniqueIDBDatabase.h"
+#include <runtime/JSCJSValue.h>
+#include <runtime/JSCJSValueInlines.h>
+#include <runtime/JSLock.h>
+#include <wtf/NeverDestroyed.h>
+
+using namespace JSC;
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<MemoryObjectStore> MemoryObjectStore::create(const IDBObjectStoreInfo& info)
+{
+ return adoptRef(*new MemoryObjectStore(info));
+}
+
+MemoryObjectStore::MemoryObjectStore(const IDBObjectStoreInfo& info)
+ : m_info(info)
+{
+}
+
+MemoryObjectStore::~MemoryObjectStore()
+{
+ m_writeTransaction = nullptr;
+}
+
+MemoryIndex* MemoryObjectStore::indexForIdentifier(uint64_t identifier)
+{
+ ASSERT(identifier);
+ return m_indexesByIdentifier.get(identifier);
+}
+
+void MemoryObjectStore::writeTransactionStarted(MemoryBackingStoreTransaction& transaction)
+{
+ LOG(IndexedDB, "MemoryObjectStore::writeTransactionStarted");
+
+ ASSERT(!m_writeTransaction);
+ m_writeTransaction = &transaction;
+}
+
+void MemoryObjectStore::writeTransactionFinished(MemoryBackingStoreTransaction& transaction)
+{
+ LOG(IndexedDB, "MemoryObjectStore::writeTransactionFinished");
+
+ ASSERT_UNUSED(transaction, m_writeTransaction == &transaction);
+ m_writeTransaction = nullptr;
+}
+
+IDBError MemoryObjectStore::createIndex(MemoryBackingStoreTransaction& transaction, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "MemoryObjectStore::createIndex");
+
+ if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction)
+ return IDBError(IDBDatabaseException::ConstraintError);
+
+ ASSERT(!m_indexesByIdentifier.contains(info.identifier()));
+ auto index = MemoryIndex::create(info, *this);
+
+ // If there was an error populating the new index, then the current records in the object store violate its contraints
+ auto error = populateIndexWithExistingRecords(index.get());
+ if (!error.isNull())
+ return error;
+
+ m_info.addExistingIndex(info);
+ transaction.addNewIndex(index.get());
+ registerIndex(WTFMove(index));
+
+ return { };
+}
+
+void MemoryObjectStore::maybeRestoreDeletedIndex(Ref<MemoryIndex>&& index)
+{
+ LOG(IndexedDB, "MemoryObjectStore::maybeRestoreDeletedIndex");
+
+ if (m_info.hasIndex(index->info().name()))
+ return;
+
+ m_info.addExistingIndex(index->info());
+
+ ASSERT(!m_indexesByIdentifier.contains(index->info().identifier()));
+ index->clearIndexValueStore();
+ auto error = populateIndexWithExistingRecords(index.get());
+
+ // Since this index was installed in the object store before this transaction started,
+ // assuming things were in a valid state then, we should definitely be able to successfully
+ // repopulate the index with the object store's pre-transaction records.
+ ASSERT_UNUSED(error, error.isNull());
+
+ registerIndex(WTFMove(index));
+}
+
+RefPtr<MemoryIndex> MemoryObjectStore::takeIndexByIdentifier(uint64_t indexIdentifier)
+{
+ auto indexByIdentifier = m_indexesByIdentifier.take(indexIdentifier);
+ if (!indexByIdentifier)
+ return nullptr;
+
+ auto index = m_indexesByName.take(indexByIdentifier->info().name());
+ ASSERT(index);
+
+ return index;
+}
+
+IDBError MemoryObjectStore::deleteIndex(MemoryBackingStoreTransaction& transaction, uint64_t indexIdentifier)
+{
+ LOG(IndexedDB, "MemoryObjectStore::deleteIndex");
+
+ if (!m_writeTransaction || !m_writeTransaction->isVersionChange() || m_writeTransaction != &transaction)
+ return IDBError(IDBDatabaseException::ConstraintError);
+
+ auto index = takeIndexByIdentifier(indexIdentifier);
+ ASSERT(index);
+ if (!index)
+ return IDBError(IDBDatabaseException::ConstraintError);
+
+ m_info.deleteIndex(indexIdentifier);
+ transaction.indexDeleted(*index);
+
+ return { };
+}
+
+void MemoryObjectStore::deleteAllIndexes(MemoryBackingStoreTransaction& transaction)
+{
+ Vector<uint64_t> indexIdentifiers;
+ indexIdentifiers.reserveInitialCapacity(m_indexesByName.size());
+
+ for (auto& index : m_indexesByName.values())
+ indexIdentifiers.uncheckedAppend(index->info().identifier());
+
+ for (auto identifier : indexIdentifiers)
+ deleteIndex(transaction, identifier);
+}
+
+bool MemoryObjectStore::containsRecord(const IDBKeyData& key)
+{
+ if (!m_keyValueStore)
+ return false;
+
+ return m_keyValueStore->contains(key);
+}
+
+void MemoryObjectStore::clear()
+{
+ LOG(IndexedDB, "MemoryObjectStore::clear");
+ ASSERT(m_writeTransaction);
+
+ m_writeTransaction->objectStoreCleared(*this, WTFMove(m_keyValueStore), WTFMove(m_orderedKeys));
+ for (auto& index : m_indexesByIdentifier.values())
+ index->objectStoreCleared();
+
+ for (auto& cursor : m_cursors.values())
+ cursor->objectStoreCleared();
+}
+
+void MemoryObjectStore::replaceKeyValueStore(std::unique_ptr<KeyValueMap>&& store, std::unique_ptr<std::set<IDBKeyData>>&& orderedKeys)
+{
+ ASSERT(m_writeTransaction);
+ ASSERT(m_writeTransaction->isAborting());
+
+ m_keyValueStore = WTFMove(store);
+ m_orderedKeys = WTFMove(orderedKeys);
+}
+
+void MemoryObjectStore::deleteRecord(const IDBKeyData& key)
+{
+ LOG(IndexedDB, "MemoryObjectStore::deleteRecord");
+
+ ASSERT(m_writeTransaction);
+
+ if (!m_keyValueStore) {
+ m_writeTransaction->recordValueChanged(*this, key, nullptr);
+ return;
+ }
+
+ ASSERT(m_orderedKeys);
+
+ auto iterator = m_keyValueStore->find(key);
+ if (iterator == m_keyValueStore->end()) {
+ m_writeTransaction->recordValueChanged(*this, key, nullptr);
+ return;
+ }
+
+ m_writeTransaction->recordValueChanged(*this, key, &iterator->value);
+ m_keyValueStore->remove(iterator);
+ m_orderedKeys->erase(key);
+
+ updateIndexesForDeleteRecord(key);
+ updateCursorsForDeleteRecord(key);
+}
+
+void MemoryObjectStore::deleteRange(const IDBKeyRangeData& inputRange)
+{
+ LOG(IndexedDB, "MemoryObjectStore::deleteRange");
+
+ ASSERT(m_writeTransaction);
+
+ if (inputRange.isExactlyOneKey()) {
+ deleteRecord(inputRange.lowerKey);
+ return;
+ }
+
+ IDBKeyRangeData range = inputRange;
+ while (true) {
+ auto key = lowestKeyWithRecordInRange(range);
+ if (key.isNull())
+ break;
+
+ deleteRecord(key);
+
+ range.lowerKey = key;
+ range.lowerOpen = true;
+ }
+}
+
+IDBError MemoryObjectStore::addRecord(MemoryBackingStoreTransaction& transaction, const IDBKeyData& keyData, const IDBValue& value)
+{
+ LOG(IndexedDB, "MemoryObjectStore::addRecord");
+
+ ASSERT(m_writeTransaction);
+ ASSERT_UNUSED(transaction, m_writeTransaction == &transaction);
+ ASSERT(!m_keyValueStore || !m_keyValueStore->contains(keyData));
+ ASSERT(!m_orderedKeys || m_orderedKeys->find(keyData) == m_orderedKeys->end());
+
+ if (!m_keyValueStore) {
+ ASSERT(!m_orderedKeys);
+ m_keyValueStore = std::make_unique<KeyValueMap>();
+ m_orderedKeys = std::make_unique<std::set<IDBKeyData>>();
+ }
+
+ auto mapResult = m_keyValueStore->set(keyData, value.data());
+ ASSERT(mapResult.isNewEntry);
+ auto listResult = m_orderedKeys->insert(keyData);
+ ASSERT(listResult.second);
+
+ // If there was an error indexing this addition, then revert it.
+ auto error = updateIndexesForPutRecord(keyData, value.data());
+ if (!error.isNull()) {
+ m_keyValueStore->remove(mapResult.iterator);
+ m_orderedKeys->erase(listResult.first);
+ } else
+ updateCursorsForPutRecord(listResult.first);
+
+ return error;
+}
+
+void MemoryObjectStore::updateCursorsForPutRecord(std::set<IDBKeyData>::iterator iterator)
+{
+ for (auto& cursor : m_cursors.values())
+ cursor->keyAdded(iterator);
+}
+
+void MemoryObjectStore::updateCursorsForDeleteRecord(const IDBKeyData& key)
+{
+ for (auto& cursor : m_cursors.values())
+ cursor->keyDeleted(key);
+}
+
+void MemoryObjectStore::updateIndexesForDeleteRecord(const IDBKeyData& value)
+{
+ for (auto& index : m_indexesByName.values())
+ index->removeEntriesWithValueKey(value);
+}
+
+IDBError MemoryObjectStore::updateIndexesForPutRecord(const IDBKeyData& key, const ThreadSafeDataBuffer& value)
+{
+ JSLockHolder locker(UniqueIDBDatabase::databaseThreadVM());
+
+ auto jsValue = deserializeIDBValueToJSValue(UniqueIDBDatabase::databaseThreadExecState(), value);
+ if (jsValue.isUndefinedOrNull())
+ return { };
+
+ IDBError error;
+ Vector<std::pair<MemoryIndex*, IndexKey>> changedIndexRecords;
+
+ for (auto& index : m_indexesByName.values()) {
+ IndexKey indexKey;
+ generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index->info(), jsValue, indexKey);
+
+ if (indexKey.isNull())
+ continue;
+
+ error = index->putIndexKey(key, indexKey);
+ if (!error.isNull())
+ break;
+
+ changedIndexRecords.append(std::make_pair(index.get(), indexKey));
+ }
+
+ // If any of the index puts failed, revert all of the ones that went through.
+ if (!error.isNull()) {
+ for (auto& record : changedIndexRecords)
+ record.first->removeRecord(key, record.second);
+ }
+
+ return error;
+}
+
+IDBError MemoryObjectStore::populateIndexWithExistingRecords(MemoryIndex& index)
+{
+ if (!m_keyValueStore)
+ return { };
+
+ JSLockHolder locker(UniqueIDBDatabase::databaseThreadVM());
+
+ for (auto iterator : *m_keyValueStore) {
+ auto jsValue = deserializeIDBValueToJSValue(UniqueIDBDatabase::databaseThreadExecState(), iterator.value);
+ if (jsValue.isUndefinedOrNull())
+ return { };
+
+ IndexKey indexKey;
+ generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index.info(), jsValue, indexKey);
+
+ if (indexKey.isNull())
+ continue;
+
+ IDBError error = index.putIndexKey(iterator.key, indexKey);
+ if (!error.isNull())
+ return error;
+ }
+
+ return { };
+}
+
+uint64_t MemoryObjectStore::countForKeyRange(uint64_t indexIdentifier, const IDBKeyRangeData& inRange) const
+{
+ LOG(IndexedDB, "MemoryObjectStore::countForKeyRange");
+
+ if (indexIdentifier) {
+ auto* index = m_indexesByIdentifier.get(indexIdentifier);
+ ASSERT(index);
+ return index->countForKeyRange(inRange);
+ }
+
+ if (!m_keyValueStore)
+ return 0;
+
+ uint64_t count = 0;
+ IDBKeyRangeData range = inRange;
+ while (true) {
+ auto key = lowestKeyWithRecordInRange(range);
+ if (key.isNull())
+ break;
+
+ ++count;
+ range.lowerKey = key;
+ range.lowerOpen = true;
+ }
+
+ return count;
+}
+
+ThreadSafeDataBuffer MemoryObjectStore::valueForKey(const IDBKeyData& key) const
+{
+ if (!m_keyValueStore)
+ return { };
+
+ return m_keyValueStore->get(key);
+}
+
+ThreadSafeDataBuffer MemoryObjectStore::valueForKeyRange(const IDBKeyRangeData& keyRangeData) const
+{
+ LOG(IndexedDB, "MemoryObjectStore::valueForKey");
+
+ IDBKeyData key = lowestKeyWithRecordInRange(keyRangeData);
+ if (key.isNull())
+ return ThreadSafeDataBuffer();
+
+ ASSERT(m_keyValueStore);
+ return m_keyValueStore->get(key);
+}
+
+void MemoryObjectStore::getAllRecords(const IDBKeyRangeData& keyRangeData, std::optional<uint32_t> count, IndexedDB::GetAllType type, IDBGetAllResult& result) const
+{
+ result = { type };
+
+ uint32_t targetCount;
+ if (count && count.value())
+ targetCount = count.value();
+ else
+ targetCount = std::numeric_limits<uint32_t>::max();
+
+ IDBKeyRangeData range = keyRangeData;
+ uint32_t currentCount = 0;
+ while (currentCount < targetCount) {
+ IDBKeyData key = lowestKeyWithRecordInRange(range);
+ if (key.isNull())
+ return;
+
+ range.lowerKey = key;
+ range.lowerOpen = true;
+
+ if (type == IndexedDB::GetAllType::Keys)
+ result.addKey(WTFMove(key));
+ else
+ result.addValue(valueForKey(key));
+
+ ++currentCount;
+ }
+}
+
+IDBGetResult MemoryObjectStore::indexValueForKeyRange(uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range) const
+{
+ LOG(IndexedDB, "MemoryObjectStore::indexValueForKeyRange");
+
+ auto* index = m_indexesByIdentifier.get(indexIdentifier);
+ ASSERT(index);
+ return index->getResultForKeyRange(recordType, range);
+}
+
+IDBKeyData MemoryObjectStore::lowestKeyWithRecordInRange(const IDBKeyRangeData& keyRangeData) const
+{
+ if (!m_keyValueStore)
+ return { };
+
+ if (keyRangeData.isExactlyOneKey() && m_keyValueStore->contains(keyRangeData.lowerKey))
+ return keyRangeData.lowerKey;
+
+ ASSERT(m_orderedKeys);
+
+ auto lowestInRange = m_orderedKeys->lower_bound(keyRangeData.lowerKey);
+
+ if (lowestInRange == m_orderedKeys->end())
+ return { };
+
+ if (keyRangeData.lowerOpen && *lowestInRange == keyRangeData.lowerKey)
+ ++lowestInRange;
+
+ if (lowestInRange == m_orderedKeys->end())
+ return { };
+
+ if (!keyRangeData.upperKey.isNull()) {
+ if (lowestInRange->compare(keyRangeData.upperKey) > 0)
+ return { };
+ if (keyRangeData.upperOpen && *lowestInRange == keyRangeData.upperKey)
+ return { };
+ }
+
+ return *lowestInRange;
+}
+
+void MemoryObjectStore::registerIndex(Ref<MemoryIndex>&& index)
+{
+ ASSERT(!m_indexesByIdentifier.contains(index->info().identifier()));
+ ASSERT(!m_indexesByName.contains(index->info().name()));
+
+ m_indexesByName.set(index->info().name(), &index.get());
+ m_indexesByIdentifier.set(index->info().identifier(), WTFMove(index));
+}
+
+void MemoryObjectStore::unregisterIndex(MemoryIndex& index)
+{
+ ASSERT(m_indexesByIdentifier.contains(index.info().identifier()));
+ ASSERT(m_indexesByName.contains(index.info().name()));
+
+ m_indexesByName.remove(index.info().name());
+ m_indexesByIdentifier.remove(index.info().identifier());
+}
+
+MemoryObjectStoreCursor* MemoryObjectStore::maybeOpenCursor(const IDBCursorInfo& info)
+{
+ auto result = m_cursors.add(info.identifier(), nullptr);
+ if (!result.isNewEntry)
+ return nullptr;
+
+ result.iterator->value = std::make_unique<MemoryObjectStoreCursor>(*this, info);
+ return result.iterator->value.get();
+}
+
+void MemoryObjectStore::renameIndex(MemoryIndex& index, const String& newName)
+{
+ ASSERT(m_indexesByName.get(index.info().name()) == &index);
+ ASSERT(!m_indexesByName.contains(newName));
+ ASSERT(m_info.infoForExistingIndex(index.info().name()));
+
+ m_info.infoForExistingIndex(index.info().name())->rename(newName);
+ m_indexesByName.set(newName, m_indexesByName.take(index.info().name()));
+ index.rename(newName);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h
new file mode 100644
index 000000000..883959e32
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyData.h"
+#include "IDBObjectStoreInfo.h"
+#include "MemoryIndex.h"
+#include "MemoryObjectStoreCursor.h"
+#include "ThreadSafeDataBuffer.h"
+#include <set>
+#include <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBError;
+class IDBGetAllResult;
+class IDBKeyData;
+class IDBValue;
+
+struct IDBKeyRangeData;
+
+namespace IndexedDB {
+enum class GetAllType;
+enum class IndexRecordType;
+}
+
+namespace IDBServer {
+
+class MemoryBackingStoreTransaction;
+
+typedef HashMap<IDBKeyData, ThreadSafeDataBuffer, IDBKeyDataHash, IDBKeyDataHashTraits> KeyValueMap;
+
+class MemoryObjectStore : public RefCounted<MemoryObjectStore> {
+public:
+ static Ref<MemoryObjectStore> create(const IDBObjectStoreInfo&);
+
+ ~MemoryObjectStore();
+
+ void writeTransactionStarted(MemoryBackingStoreTransaction&);
+ void writeTransactionFinished(MemoryBackingStoreTransaction&);
+ MemoryBackingStoreTransaction* writeTransaction() { return m_writeTransaction; }
+
+ IDBError createIndex(MemoryBackingStoreTransaction&, const IDBIndexInfo&);
+ IDBError deleteIndex(MemoryBackingStoreTransaction&, uint64_t indexIdentifier);
+ void deleteAllIndexes(MemoryBackingStoreTransaction&);
+ void registerIndex(Ref<MemoryIndex>&&);
+
+ bool containsRecord(const IDBKeyData&);
+ void deleteRecord(const IDBKeyData&);
+ void deleteRange(const IDBKeyRangeData&);
+ IDBError addRecord(MemoryBackingStoreTransaction&, const IDBKeyData&, const IDBValue&);
+
+ uint64_t currentKeyGeneratorValue() const { return m_keyGeneratorValue; }
+ void setKeyGeneratorValue(uint64_t value) { m_keyGeneratorValue = value; }
+
+ void clear();
+ void replaceKeyValueStore(std::unique_ptr<KeyValueMap>&&, std::unique_ptr<std::set<IDBKeyData>>&&);
+
+ ThreadSafeDataBuffer valueForKey(const IDBKeyData&) const;
+ ThreadSafeDataBuffer valueForKeyRange(const IDBKeyRangeData&) const;
+ IDBKeyData lowestKeyWithRecordInRange(const IDBKeyRangeData&) const;
+ IDBGetResult indexValueForKeyRange(uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&) const;
+ uint64_t countForKeyRange(uint64_t indexIdentifier, const IDBKeyRangeData&) const;
+
+ void getAllRecords(const IDBKeyRangeData&, std::optional<uint32_t> count, IndexedDB::GetAllType, IDBGetAllResult&) const;
+
+ const IDBObjectStoreInfo& info() const { return m_info; }
+
+ MemoryObjectStoreCursor* maybeOpenCursor(const IDBCursorInfo&);
+
+ std::set<IDBKeyData>* orderedKeys() { return m_orderedKeys.get(); }
+
+ MemoryIndex* indexForIdentifier(uint64_t);
+
+ void maybeRestoreDeletedIndex(Ref<MemoryIndex>&&);
+
+ void rename(const String& newName) { m_info.rename(newName); }
+ void renameIndex(MemoryIndex&, const String& newName);
+
+private:
+ MemoryObjectStore(const IDBObjectStoreInfo&);
+
+ std::set<IDBKeyData>::iterator lowestIteratorInRange(const IDBKeyRangeData&, bool reverse) const;
+
+ IDBError populateIndexWithExistingRecords(MemoryIndex&);
+ IDBError updateIndexesForPutRecord(const IDBKeyData&, const ThreadSafeDataBuffer& value);
+ void updateIndexesForDeleteRecord(const IDBKeyData& value);
+ void updateCursorsForPutRecord(std::set<IDBKeyData>::iterator);
+ void updateCursorsForDeleteRecord(const IDBKeyData&);
+
+ RefPtr<MemoryIndex> takeIndexByIdentifier(uint64_t indexIdentifier);
+
+ IDBObjectStoreInfo m_info;
+
+ MemoryBackingStoreTransaction* m_writeTransaction { nullptr };
+ uint64_t m_keyGeneratorValue { 1 };
+
+ std::unique_ptr<KeyValueMap> m_keyValueStore;
+ std::unique_ptr<std::set<IDBKeyData>> m_orderedKeys;
+
+ void unregisterIndex(MemoryIndex&);
+ HashMap<uint64_t, RefPtr<MemoryIndex>> m_indexesByIdentifier;
+ HashMap<String, RefPtr<MemoryIndex>> m_indexesByName;
+ HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryObjectStoreCursor>> m_cursors;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp
new file mode 100644
index 000000000..d95efe6a9
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "MemoryObjectStoreCursor.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBGetResult.h"
+#include "Logging.h"
+#include "MemoryObjectStore.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+MemoryObjectStoreCursor::MemoryObjectStoreCursor(MemoryObjectStore& objectStore, const IDBCursorInfo& info)
+ : MemoryCursor(info)
+ , m_objectStore(objectStore)
+ , m_remainingRange(info.range())
+{
+ LOG(IndexedDB, "MemoryObjectStoreCursor::MemoryObjectStoreCursor %s", info.range().loggingString().utf8().data());
+
+ auto* orderedKeys = objectStore.orderedKeys();
+ if (!orderedKeys)
+ return;
+
+ setFirstInRemainingRange(*orderedKeys);
+}
+
+void MemoryObjectStoreCursor::objectStoreCleared()
+{
+ m_iterator = std::nullopt;
+}
+
+void MemoryObjectStoreCursor::keyDeleted(const IDBKeyData& key)
+{
+ if (m_currentPositionKey != key)
+ return;
+
+ m_iterator = std::nullopt;
+}
+
+void MemoryObjectStoreCursor::keyAdded(std::set<IDBKeyData>::iterator iterator)
+{
+ if (m_iterator)
+ return;
+
+ if (*iterator == m_currentPositionKey)
+ m_iterator = iterator;
+}
+
+void MemoryObjectStoreCursor::setFirstInRemainingRange(std::set<IDBKeyData>& set)
+{
+ m_iterator = std::nullopt;
+
+ if (m_info.isDirectionForward()) {
+ setForwardIteratorFromRemainingRange(set);
+ if (m_iterator) {
+ m_remainingRange.lowerKey = **m_iterator;
+ m_remainingRange.lowerOpen = true;
+ }
+ } else {
+ setReverseIteratorFromRemainingRange(set);
+ if (m_iterator) {
+ m_remainingRange.upperKey = **m_iterator;
+ m_remainingRange.upperOpen = true;
+ }
+ }
+
+ ASSERT(!m_iterator || *m_iterator != set.end());
+}
+
+void MemoryObjectStoreCursor::setForwardIteratorFromRemainingRange(std::set<IDBKeyData>& set)
+{
+ if (!set.size()) {
+ m_iterator = std::nullopt;
+ return;
+ }
+
+ if (m_remainingRange.isExactlyOneKey()) {
+ m_iterator = set.find(m_remainingRange.lowerKey);
+ if (*m_iterator == set.end())
+ m_iterator = std::nullopt;
+
+ return;
+ }
+
+ m_iterator = std::nullopt;
+
+ auto lowest = set.lower_bound(m_remainingRange.lowerKey);
+ if (lowest == set.end())
+ return;
+
+ if (m_remainingRange.lowerOpen && *lowest == m_remainingRange.lowerKey) {
+ ++lowest;
+ if (lowest == set.end())
+ return;
+ }
+
+ if (!m_remainingRange.upperKey.isNull()) {
+ if (lowest->compare(m_remainingRange.upperKey) > 0)
+ return;
+
+ if (m_remainingRange.upperOpen && *lowest == m_remainingRange.upperKey)
+ return;
+ }
+
+ m_iterator = lowest;
+}
+
+void MemoryObjectStoreCursor::setReverseIteratorFromRemainingRange(std::set<IDBKeyData>& set)
+{
+ if (!set.size()) {
+ m_iterator = std::nullopt;
+ return;
+ }
+
+ if (m_remainingRange.isExactlyOneKey()) {
+ m_iterator = set.find(m_remainingRange.lowerKey);
+ if (*m_iterator == set.end())
+ m_iterator = std::nullopt;
+
+ return;
+ }
+
+ if (!m_remainingRange.upperKey.isValid()) {
+ m_iterator = --set.end();
+ if (!m_remainingRange.containsKey(**m_iterator))
+ m_iterator = std::nullopt;
+
+ return;
+ }
+
+ m_iterator = std::nullopt;
+
+ // This is one record past the actual key we're looking for.
+ auto highest = set.upper_bound(m_remainingRange.upperKey);
+
+ if (highest == set.begin())
+ return;
+
+ // This is one record before that, which *is* the key we're looking for.
+ --highest;
+
+ if (m_remainingRange.upperOpen && *highest == m_remainingRange.upperKey) {
+ if (highest == set.begin())
+ return;
+ --highest;
+ }
+
+ if (!m_remainingRange.lowerKey.isNull()) {
+ if (highest->compare(m_remainingRange.lowerKey) < 0)
+ return;
+
+ if (m_remainingRange.lowerOpen && *highest == m_remainingRange.lowerKey)
+ return;
+ }
+
+ m_iterator = highest;
+}
+
+void MemoryObjectStoreCursor::currentData(IDBGetResult& data)
+{
+ if (!m_iterator) {
+ m_currentPositionKey = { };
+ data = { };
+ return;
+ }
+
+ m_currentPositionKey = **m_iterator;
+ if (m_info.cursorType() == IndexedDB::CursorType::KeyOnly)
+ data = { m_currentPositionKey, m_currentPositionKey };
+ else {
+ IDBValue value = { m_objectStore.valueForKeyRange(m_currentPositionKey), { }, { } };
+ data = { m_currentPositionKey, m_currentPositionKey, WTFMove(value) };
+ }
+}
+
+void MemoryObjectStoreCursor::incrementForwardIterator(std::set<IDBKeyData>& set, const IDBKeyData& key, uint32_t count)
+{
+ // We might need to re-grab the current iterator.
+ // e.g. If the record it was pointed to had been deleted.
+ bool didResetIterator = false;
+ if (!m_iterator) {
+ if (!m_currentPositionKey.isValid())
+ return;
+
+ m_remainingRange.lowerKey = m_currentPositionKey;
+ m_remainingRange.lowerOpen = false;
+ setFirstInRemainingRange(set);
+
+ didResetIterator = true;
+ }
+
+ if (!m_iterator)
+ return;
+
+ ASSERT(*m_iterator != set.end());
+
+ if (key.isValid()) {
+ // If iterating to a key, the count passed in must be zero, as there is no way to iterate by both a count and to a key.
+ ASSERT(!count);
+
+ if (!m_info.range().containsKey(key))
+ return;
+
+ if ((*m_iterator)->compare(key) < 0) {
+ m_remainingRange.lowerKey = key;
+ m_remainingRange.lowerOpen = false;
+ setFirstInRemainingRange(set);
+ }
+
+ return;
+ }
+
+ if (!count)
+ count = 1;
+
+ // If the forwardIterator was reset because it's previous record had been deleted,
+ // we might have already advanced past the current position, eating one one of the iteration counts.
+ if (didResetIterator && (*m_iterator)->compare(m_currentPositionKey) > 0)
+ --count;
+
+ while (count) {
+ --count;
+ ++*m_iterator;
+
+ if (*m_iterator == set.end() || !m_info.range().containsKey(**m_iterator)) {
+ m_iterator = std::nullopt;
+ return;
+ }
+ }
+}
+
+void MemoryObjectStoreCursor::incrementReverseIterator(std::set<IDBKeyData>& set, const IDBKeyData& key, uint32_t count)
+{
+ // We might need to re-grab the current iterator.
+ // e.g. If the record it was pointed to had been deleted.
+ bool didResetIterator = false;
+ if (!m_iterator) {
+ if (!m_currentPositionKey.isValid())
+ return;
+
+ m_remainingRange.upperKey = m_currentPositionKey;
+ m_remainingRange.upperOpen = false;
+ setFirstInRemainingRange(set);
+
+ didResetIterator = true;
+ }
+
+ if (*m_iterator == set.end())
+ return;
+
+ if (key.isValid()) {
+ // If iterating to a key, the count passed in must be zero, as there is no way to iterate by both a count and to a key.
+ ASSERT(!count);
+
+ if (!m_info.range().containsKey(key))
+ return;
+
+ if ((*m_iterator)->compare(key) > 0) {
+ m_remainingRange.upperKey = key;
+ m_remainingRange.upperOpen = false;
+
+ setFirstInRemainingRange(set);
+ }
+
+ return;
+ }
+
+ if (!count)
+ count = 1;
+
+ // If the reverseIterator was reset because it's previous record had been deleted,
+ // we might have already advanced past the current position, eating one one of the iteration counts.
+ if (didResetIterator && (*m_iterator)->compare(m_currentPositionKey) < 0)
+ --count;
+
+ while (count) {
+ if (*m_iterator == set.begin()) {
+ m_iterator = std::nullopt;
+ return;
+ }
+
+ --count;
+ --*m_iterator;
+
+ if (!m_info.range().containsKey(**m_iterator)) {
+ m_iterator = std::nullopt;
+ return;
+ }
+ }
+}
+
+void MemoryObjectStoreCursor::iterate(const IDBKeyData& key, const IDBKeyData& primaryKeyData, uint32_t count, IDBGetResult& outData)
+{
+ LOG(IndexedDB, "MemoryObjectStoreCursor::iterate to key %s", key.loggingString().utf8().data());
+
+ ASSERT_UNUSED(primaryKeyData, primaryKeyData.isNull());
+
+ if (!m_objectStore.orderedKeys()) {
+ m_currentPositionKey = { };
+ outData = { };
+ return;
+ }
+
+ if (key.isValid() && !m_info.range().containsKey(key)) {
+ m_currentPositionKey = { };
+ outData = { };
+ return;
+ }
+
+ auto* set = m_objectStore.orderedKeys();
+ if (set) {
+ if (m_info.isDirectionForward())
+ incrementForwardIterator(*set, key, count);
+ else
+ incrementReverseIterator(*set, key, count);
+ }
+
+ m_currentPositionKey = { };
+
+ if (!m_iterator) {
+ outData = { };
+ return;
+ }
+
+ currentData(outData);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h
new file mode 100644
index 000000000..633f1e66c
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IDBKeyData.h"
+#include "MemoryCursor.h"
+#include <set>
+#include <wtf/Optional.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+class MemoryObjectStore;
+
+class MemoryObjectStoreCursor : public MemoryCursor {
+public:
+ MemoryObjectStoreCursor(MemoryObjectStore&, const IDBCursorInfo&);
+
+ void objectStoreCleared();
+ void keyDeleted(const IDBKeyData&);
+ void keyAdded(std::set<IDBKeyData>::iterator);
+
+private:
+ void currentData(IDBGetResult&) final;
+ void iterate(const IDBKeyData&, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult&) final;
+
+ void setFirstInRemainingRange(std::set<IDBKeyData>&);
+ void setForwardIteratorFromRemainingRange(std::set<IDBKeyData>&);
+ void setReverseIteratorFromRemainingRange(std::set<IDBKeyData>&);
+
+ void incrementForwardIterator(std::set<IDBKeyData>&, const IDBKeyData&, uint32_t count);
+ void incrementReverseIterator(std::set<IDBKeyData>&, const IDBKeyData&, uint32_t count);
+
+ bool hasValidPosition() const;
+
+ MemoryObjectStore& m_objectStore;
+
+ IDBKeyRangeData m_remainingRange;
+
+ std::optional<std::set<IDBKeyData>::iterator> m_iterator;
+
+ IDBKeyData m_currentPositionKey;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp
new file mode 100644
index 000000000..fa3755e60
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp
@@ -0,0 +1,2601 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "SQLiteIDBBackingStore.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "FileSystem.h"
+#include "IDBBindingUtilities.h"
+#include "IDBDatabaseException.h"
+#include "IDBGetAllRecordsData.h"
+#include "IDBGetAllResult.h"
+#include "IDBGetRecordData.h"
+#include "IDBGetResult.h"
+#include "IDBIterateCursorData.h"
+#include "IDBKeyData.h"
+#include "IDBObjectStoreInfo.h"
+#include "IDBSerialization.h"
+#include "IDBTransactionInfo.h"
+#include "IDBValue.h"
+#include "IndexKey.h"
+#include "Logging.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteFileSystem.h"
+#include "SQLiteIDBCursor.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include "ThreadSafeDataBuffer.h"
+#include <heap/HeapInlines.h>
+#include <heap/StrongInlines.h>
+#include <runtime/AuxiliaryBarrierInlines.h>
+#include <runtime/JSCJSValueInlines.h>
+#include <runtime/JSGlobalObject.h>
+#include <runtime/StructureInlines.h>
+#include <wtf/NeverDestroyed.h>
+
+using namespace JSC;
+
+namespace WebCore {
+namespace IDBServer {
+
+// Current version of the metadata schema being used in the metadata database.
+static const int currentMetadataVersion = 1;
+
+static int idbKeyCollate(int aLength, const void* aBuffer, int bLength, const void* bBuffer)
+{
+ IDBKeyData a, b;
+ if (!deserializeIDBKeyData(static_cast<const uint8_t*>(aBuffer), aLength, a)) {
+ LOG_ERROR("Unable to deserialize key A in collation function.");
+
+ // There's no way to indicate an error to SQLite - we have to return a sorting decision.
+ // We arbitrarily choose "A > B"
+ return 1;
+ }
+ if (!deserializeIDBKeyData(static_cast<const uint8_t*>(bBuffer), bLength, b)) {
+ LOG_ERROR("Unable to deserialize key B in collation function.");
+
+ // There's no way to indicate an error to SQLite - we have to return a sorting decision.
+ // We arbitrarily choose "A > B"
+ return 1;
+ }
+
+ return a.compare(b);
+}
+
+static const String v1RecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value NOT NULL ON CONFLICT FAIL)");
+}
+
+static const String& v1RecordsTableSchema()
+{
+ static NeverDestroyed<WTF::String> v1RecordsTableSchemaString(v1RecordsTableSchema("Records"));
+ return v1RecordsTableSchemaString;
+}
+
+static const String& v1RecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> v1RecordsTableSchemaString(v1RecordsTableSchema("\"Records\""));
+ return v1RecordsTableSchemaString;
+}
+
+static const String v2RecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL)");
+}
+
+static const String& v2RecordsTableSchema()
+{
+ static NeverDestroyed<WTF::String> v2RecordsTableSchemaString(v2RecordsTableSchema("Records"));
+ return v2RecordsTableSchemaString;
+}
+
+static const String& v2RecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> v2RecordsTableSchemaString(v2RecordsTableSchema("\"Records\""));
+ return v2RecordsTableSchemaString;
+}
+
+static const String v3RecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL, recordID INTEGER PRIMARY KEY)");
+}
+
+static const String& v3RecordsTableSchema()
+{
+ static NeverDestroyed<WTF::String> v3RecordsTableSchemaString(v3RecordsTableSchema("Records"));
+ return v3RecordsTableSchemaString;
+}
+
+static const String& v3RecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> v3RecordsTableSchemaString(v3RecordsTableSchema("\"Records\""));
+ return v3RecordsTableSchemaString;
+}
+
+static const String v1IndexRecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value NOT NULL ON CONFLICT FAIL)");
+}
+
+static const String& v1IndexRecordsTableSchema()
+{
+ static NeverDestroyed<WTF::String> v1IndexRecordsTableSchemaString(v1IndexRecordsTableSchema("IndexRecords"));
+ return v1IndexRecordsTableSchemaString;
+}
+
+static const String& v1IndexRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> v1IndexRecordsTableSchemaString(v1IndexRecordsTableSchema("\"IndexRecords\""));
+ return v1IndexRecordsTableSchemaString;
+}
+
+static const String v2IndexRecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL)");
+}
+
+static const String& v2IndexRecordsTableSchema()
+{
+ static NeverDestroyed<WTF::String> v2IndexRecordsTableSchemaString(v2IndexRecordsTableSchema("IndexRecords"));
+ return v2IndexRecordsTableSchemaString;
+}
+
+static const String& v2IndexRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> v2IndexRecordsTableSchemaString(v2IndexRecordsTableSchema("\"IndexRecords\""));
+ return v2IndexRecordsTableSchemaString;
+}
+
+static const String v3IndexRecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (indexID INTEGER NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, value TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL, objectStoreRecordID INTEGER NOT NULL ON CONFLICT FAIL)");
+}
+
+static const String v3IndexRecordsTableSchema()
+{
+ static NeverDestroyed<WTF::String> indexRecordsTableSchemaString = v3IndexRecordsTableSchema("IndexRecords");
+ return indexRecordsTableSchemaString;
+}
+
+static const String v3IndexRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> indexRecordsTableSchemaString = v3IndexRecordsTableSchema("\"IndexRecords\"");
+ return indexRecordsTableSchemaString;
+}
+
+static const String& v1IndexRecordsIndexSchema()
+{
+ static NeverDestroyed<WTF::String> indexRecordsIndexSchemaString("CREATE INDEX IndexRecordsIndex ON IndexRecords (key)");
+ return indexRecordsIndexSchemaString;
+}
+
+static const String blobRecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (objectStoreRow INTEGER NOT NULL ON CONFLICT FAIL, blobURL TEXT NOT NULL ON CONFLICT FAIL)");
+}
+
+static const String& blobRecordsTableSchema()
+{
+ static NeverDestroyed<String> blobRecordsTableSchemaString(blobRecordsTableSchema("BlobRecords"));
+ return blobRecordsTableSchemaString;
+}
+
+static const String& blobRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<String> blobRecordsTableSchemaString(blobRecordsTableSchema("\"BlobRecords\""));
+ return blobRecordsTableSchemaString;
+}
+
+static const String blobFilesTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (blobURL TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, fileName TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL)");
+}
+
+static const String& blobFilesTableSchema()
+{
+ static NeverDestroyed<String> blobFilesTableSchemaString(blobFilesTableSchema("BlobFiles"));
+ return blobFilesTableSchemaString;
+}
+
+static const String& blobFilesTableSchemaAlternate()
+{
+ static NeverDestroyed<String> blobFilesTableSchemaString(blobFilesTableSchema("\"BlobFiles\""));
+ return blobFilesTableSchemaString;
+}
+
+SQLiteIDBBackingStore::SQLiteIDBBackingStore(const IDBDatabaseIdentifier& identifier, const String& databaseRootDirectory, IDBBackingStoreTemporaryFileHandler& fileHandler)
+ : m_identifier(identifier)
+ , m_temporaryFileHandler(fileHandler)
+{
+ m_absoluteDatabaseDirectory = identifier.databaseDirectoryRelativeToRoot(databaseRootDirectory);
+}
+
+SQLiteIDBBackingStore::~SQLiteIDBBackingStore()
+{
+ if (m_sqliteDB)
+ closeSQLiteDB();
+
+ if (m_vm) {
+ JSLockHolder locker(m_vm.get());
+ m_globalObject.clear();
+ m_vm = nullptr;
+ }
+}
+
+
+void SQLiteIDBBackingStore::initializeVM()
+{
+ if (!m_vm) {
+ ASSERT(!m_globalObject);
+ m_vm = VM::create();
+
+ JSLockHolder locker(m_vm.get());
+ m_globalObject.set(*m_vm, JSGlobalObject::create(*m_vm, JSGlobalObject::createStructure(*m_vm, jsNull())));
+ }
+}
+
+VM& SQLiteIDBBackingStore::vm()
+{
+ initializeVM();
+ return *m_vm;
+}
+
+JSGlobalObject& SQLiteIDBBackingStore::globalObject()
+{
+ initializeVM();
+ return **m_globalObject;
+}
+
+static bool createOrMigrateRecordsTableIfNecessary(SQLiteDatabase& database)
+{
+ String currentSchema;
+ {
+ // Fetch the schema for an existing records table.
+ SQLiteStatement statement(database, "SELECT type, sql FROM sqlite_master WHERE tbl_name='Records'");
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare statement to fetch schema for the Records table.");
+ return false;
+ }
+
+ int sqliteResult = statement.step();
+
+ // If there is no Records table at all, create it and then bail.
+ if (sqliteResult == SQLITE_DONE) {
+ if (!database.executeCommand(v3RecordsTableSchema())) {
+ LOG_ERROR("Could not create Records table in database (%i) - %s", database.lastError(), database.lastErrorMsg());
+ return false;
+ }
+
+ return true;
+ }
+
+ if (sqliteResult != SQLITE_ROW) {
+ LOG_ERROR("Error executing statement to fetch schema for the Records table.");
+ return false;
+ }
+
+ currentSchema = statement.getColumnText(1);
+ }
+
+ ASSERT(!currentSchema.isEmpty());
+
+ // If the schema in the backing store is the current schema, we're done.
+ if (currentSchema == v3RecordsTableSchema() || currentSchema == v3RecordsTableSchemaAlternate())
+ return true;
+
+ // If the record table is not the current schema then it must be one of the previous schemas.
+ // If it is not then the database is in an unrecoverable state and this should be considered a fatal error.
+ if (currentSchema != v1RecordsTableSchema() && currentSchema != v1RecordsTableSchemaAlternate()
+ && currentSchema != v2RecordsTableSchema() && currentSchema != v2RecordsTableSchemaAlternate())
+ RELEASE_ASSERT_NOT_REACHED();
+
+ SQLiteTransaction transaction(database);
+ transaction.begin();
+
+ // Create a temporary table with the correct schema and migrate all existing content over.
+ if (!database.executeCommand(v3RecordsTableSchema("_Temp_Records"))) {
+ LOG_ERROR("Could not create temporary records table in database (%i) - %s", database.lastError(), database.lastErrorMsg());
+ return false;
+ }
+
+ if (!database.executeCommand("INSERT INTO _Temp_Records (objectStoreID, key, value) SELECT objectStoreID, CAST(key AS TEXT), value FROM Records")) {
+ LOG_ERROR("Could not migrate existing Records content (%i) - %s", database.lastError(), database.lastErrorMsg());
+ return false;
+ }
+
+ if (!database.executeCommand("DROP TABLE Records")) {
+ LOG_ERROR("Could not drop existing Records table (%i) - %s", database.lastError(), database.lastErrorMsg());
+ return false;
+ }
+
+ if (!database.executeCommand("ALTER TABLE _Temp_Records RENAME TO Records")) {
+ LOG_ERROR("Could not rename temporary Records table (%i) - %s", database.lastError(), database.lastErrorMsg());
+ return false;
+ }
+
+ transaction.commit();
+
+ return true;
+}
+
+bool SQLiteIDBBackingStore::ensureValidBlobTables()
+{
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ String currentSchema;
+ {
+ // Fetch the schema for an existing blob record table.
+ SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobRecords'");
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare statement to fetch schema for the BlobRecords table.");
+ return false;
+ }
+
+ int sqliteResult = statement.step();
+
+ // If there is no BlobRecords table at all, create it..
+ if (sqliteResult == SQLITE_DONE) {
+ if (!m_sqliteDB->executeCommand(blobRecordsTableSchema())) {
+ LOG_ERROR("Could not create BlobRecords table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ currentSchema = blobRecordsTableSchema();
+ } else if (sqliteResult != SQLITE_ROW) {
+ LOG_ERROR("Error executing statement to fetch schema for the BlobRecords table.");
+ return false;
+ } else
+ currentSchema = statement.getColumnText(1);
+ }
+
+ if (currentSchema != blobRecordsTableSchema() && currentSchema != blobRecordsTableSchemaAlternate()) {
+ LOG_ERROR("Invalid BlobRecords table schema found");
+ return false;
+ }
+
+ {
+ // Fetch the schema for an existing blob file table.
+ SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobFiles'");
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare statement to fetch schema for the BlobFiles table.");
+ return false;
+ }
+
+ int sqliteResult = statement.step();
+
+ // If there is no BlobFiles table at all, create it and then bail.
+ if (sqliteResult == SQLITE_DONE) {
+ if (!m_sqliteDB->executeCommand(blobFilesTableSchema())) {
+ LOG_ERROR("Could not create BlobFiles table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ return true;
+ }
+
+ if (sqliteResult != SQLITE_ROW) {
+ LOG_ERROR("Error executing statement to fetch schema for the BlobFiles table.");
+ return false;
+ }
+
+ currentSchema = statement.getColumnText(1);
+ }
+
+ if (currentSchema != blobFilesTableSchema() && currentSchema != blobFilesTableSchemaAlternate()) {
+ LOG_ERROR("Invalid BlobFiles table schema found");
+ return false;
+ }
+
+ return true;
+}
+
+bool SQLiteIDBBackingStore::ensureValidRecordsTable()
+{
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ if (!createOrMigrateRecordsTableIfNecessary(*m_sqliteDB))
+ return false;
+
+ // Whether the updated records table already existed or if it was just created and the data migrated over,
+ // make sure the uniqueness index exists.
+ if (!m_sqliteDB->executeCommand("CREATE UNIQUE INDEX IF NOT EXISTS RecordsIndex ON Records (objectStoreID, key);")) {
+ LOG_ERROR("Could not create RecordsIndex on Records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ return true;
+}
+
+bool SQLiteIDBBackingStore::ensureValidIndexRecordsTable()
+{
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ String currentSchema;
+ {
+ // Fetch the schema for an existing index record table.
+ SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='IndexRecords'");
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare statement to fetch schema for the IndexRecords table.");
+ return false;
+ }
+
+ int sqliteResult = statement.step();
+
+ // If there is no IndexRecords table at all, create it and then bail.
+ if (sqliteResult == SQLITE_DONE) {
+ if (!m_sqliteDB->executeCommand(v3IndexRecordsTableSchema())) {
+ LOG_ERROR("Could not create IndexRecords table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ return true;
+ }
+
+ if (sqliteResult != SQLITE_ROW) {
+ LOG_ERROR("Error executing statement to fetch schema for the IndexRecords table.");
+ return false;
+ }
+
+ currentSchema = statement.getColumnText(1);
+ }
+
+ ASSERT(!currentSchema.isEmpty());
+
+ // If the schema in the backing store is the current schema, we're done.
+ if (currentSchema == v3IndexRecordsTableSchema() || currentSchema == v3IndexRecordsTableSchemaAlternate())
+ return true;
+
+ // If the record table is not the current schema then it must be one of the previous schemas.
+ // If it is not then the database is in an unrecoverable state and this should be considered a fatal error.
+ if (currentSchema != v1IndexRecordsTableSchema() && currentSchema != v1IndexRecordsTableSchemaAlternate()
+ && currentSchema != v2IndexRecordsTableSchema() && currentSchema != v2IndexRecordsTableSchemaAlternate())
+ RELEASE_ASSERT_NOT_REACHED();
+
+ SQLiteTransaction transaction(*m_sqliteDB);
+ transaction.begin();
+
+ // Create a temporary table with the correct schema and migrate all existing content over.
+ if (!m_sqliteDB->executeCommand(v3IndexRecordsTableSchema("_Temp_IndexRecords"))) {
+ LOG_ERROR("Could not create temporary index records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ if (!m_sqliteDB->executeCommand("INSERT INTO _Temp_IndexRecords SELECT IndexRecords.indexID, IndexRecords.objectStoreID, IndexRecords.key, IndexRecords.value, Records.rowid FROM IndexRecords INNER JOIN Records ON Records.key = IndexRecords.value AND Records.objectStoreID = IndexRecords.objectStoreID")) {
+ LOG_ERROR("Could not migrate existing IndexRecords content (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ if (!m_sqliteDB->executeCommand("DROP TABLE IndexRecords")) {
+ LOG_ERROR("Could not drop existing IndexRecords table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ if (!m_sqliteDB->executeCommand("ALTER TABLE _Temp_IndexRecords RENAME TO IndexRecords")) {
+ LOG_ERROR("Could not rename temporary IndexRecords table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ transaction.commit();
+
+ return true;
+}
+
+bool SQLiteIDBBackingStore::ensureValidIndexRecordsIndex()
+{
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ String currentSchema;
+ {
+ // Fetch the schema for an existing index record index.
+ SQLiteStatement statement(*m_sqliteDB, "SELECT sql FROM sqlite_master WHERE name='IndexRecordsIndex'");
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare statement to fetch schema for the IndexRecordsIndex index.");
+ return false;
+ }
+
+ int sqliteResult = statement.step();
+
+ // If there is no IndexRecordsIndex index at all, create it and then bail.
+ if (sqliteResult == SQLITE_DONE) {
+ if (!m_sqliteDB->executeCommand(v1IndexRecordsIndexSchema())) {
+ LOG_ERROR("Could not create IndexRecordsIndex index in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ return true;
+ }
+
+ if (sqliteResult != SQLITE_ROW) {
+ LOG_ERROR("Error executing statement to fetch schema for the IndexRecordsIndex index.");
+ return false;
+ }
+
+ currentSchema = statement.getColumnText(0);
+ }
+
+ ASSERT(!currentSchema.isEmpty());
+
+ // If the schema in the backing store is the current schema, we're done.
+ if (currentSchema == v1IndexRecordsIndexSchema())
+ return true;
+
+ // There is currently no outdated schema for the IndexRecordsIndex, so any other existing schema means this database is invalid.
+ return false;
+}
+
+std::unique_ptr<IDBDatabaseInfo> SQLiteIDBBackingStore::createAndPopulateInitialDatabaseInfo()
+{
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ if (!m_sqliteDB->executeCommand("CREATE TABLE IDBDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL);")) {
+ LOG_ERROR("Could not create IDBDatabaseInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+
+ if (!m_sqliteDB->executeCommand("CREATE TABLE ObjectStoreInfo (id INTEGER PRIMARY KEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, autoInc INTEGER NOT NULL ON CONFLICT FAIL, maxIndexID INTEGER NOT NULL ON CONFLICT FAIL);")) {
+ LOG_ERROR("Could not create ObjectStoreInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+
+ if (!m_sqliteDB->executeCommand("CREATE TABLE IndexInfo (id INTEGER NOT NULL ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, isUnique INTEGER NOT NULL ON CONFLICT FAIL, multiEntry INTEGER NOT NULL ON CONFLICT FAIL);")) {
+ LOG_ERROR("Could not create IndexInfo table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+
+ if (!m_sqliteDB->executeCommand("CREATE TABLE KeyGenerators (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, currentKey INTEGER NOT NULL ON CONFLICT FAIL);")) {
+ LOG_ERROR("Could not create KeyGenerators table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MetadataVersion', ?);"));
+ if (sql.prepare() != SQLITE_OK
+ || sql.bindInt(1, currentMetadataVersion) != SQLITE_OK
+ || sql.step() != SQLITE_DONE) {
+ LOG_ERROR("Could not insert database metadata version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+ }
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseName', ?);"));
+ if (sql.prepare() != SQLITE_OK
+ || sql.bindText(1, m_identifier.databaseName()) != SQLITE_OK
+ || sql.step() != SQLITE_DONE) {
+ LOG_ERROR("Could not insert database name into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+ }
+ {
+ // Database versions are defined to be a uin64_t in the spec but sqlite3 doesn't support native binding of unsigned integers.
+ // Therefore we'll store the version as a String.
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('DatabaseVersion', ?);"));
+ if (sql.prepare() != SQLITE_OK
+ || sql.bindText(1, String::number(0)) != SQLITE_OK
+ || sql.step() != SQLITE_DONE) {
+ LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+ }
+
+ if (!m_sqliteDB->executeCommand(ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MaxObjectStoreID', 1);"))) {
+ LOG_ERROR("Could not insert default version into IDBDatabaseInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ closeSQLiteDB();
+ return nullptr;
+ }
+
+ // This initial database info matches the default values we just put into the metadata database.
+ return std::make_unique<IDBDatabaseInfo>(m_identifier.databaseName(), 0);
+}
+
+std::unique_ptr<IDBDatabaseInfo> SQLiteIDBBackingStore::extractExistingDatabaseInfo()
+{
+ ASSERT(m_sqliteDB);
+
+ if (!m_sqliteDB->tableExists(ASCIILiteral("IDBDatabaseInfo")))
+ return nullptr;
+
+ String databaseName;
+ {
+ SQLiteStatement sql(*m_sqliteDB, "SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseName';");
+ if (sql.isColumnNull(0))
+ return nullptr;
+ databaseName = sql.getColumnText(0);
+ if (databaseName != m_identifier.databaseName()) {
+ LOG_ERROR("Database name in the info database ('%s') does not match the expected name ('%s')", databaseName.utf8().data(), m_identifier.databaseName().utf8().data());
+ return nullptr;
+ }
+ }
+ uint64_t databaseVersion;
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT value FROM IDBDatabaseInfo WHERE key = 'DatabaseVersion';"));
+ if (sql.isColumnNull(0))
+ return nullptr;
+ String stringVersion = sql.getColumnText(0);
+ bool ok;
+ databaseVersion = stringVersion.toUInt64Strict(&ok);
+ if (!ok) {
+ LOG_ERROR("Database version on disk ('%s') does not cleanly convert to an unsigned 64-bit integer version", stringVersion.utf8().data());
+ return nullptr;
+ }
+ }
+
+ auto databaseInfo = std::make_unique<IDBDatabaseInfo>(databaseName, databaseVersion);
+
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id, name, keyPath, autoInc, maxIndexID FROM ObjectStoreInfo;"));
+ if (sql.prepare() != SQLITE_OK)
+ return nullptr;
+
+ int result = sql.step();
+ while (result == SQLITE_ROW) {
+ uint64_t objectStoreID = sql.getColumnInt64(0);
+ String objectStoreName = sql.getColumnText(1);
+
+ Vector<char> keyPathBuffer;
+ sql.getColumnBlobAsVector(2, keyPathBuffer);
+
+ std::optional<IDBKeyPath> objectStoreKeyPath;
+ if (!deserializeIDBKeyPath(reinterpret_cast<const uint8_t*>(keyPathBuffer.data()), keyPathBuffer.size(), objectStoreKeyPath)) {
+ LOG_ERROR("Unable to extract key path from database");
+ return nullptr;
+ }
+
+ bool autoIncrement = sql.getColumnInt(3);
+
+ databaseInfo->addExistingObjectStore({ objectStoreID, objectStoreName, WTFMove(objectStoreKeyPath), autoIncrement });
+
+ result = sql.step();
+ }
+
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Error fetching object store info from database on disk");
+ return nullptr;
+ }
+ }
+
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT id, name, objectStoreID, keyPath, isUnique, multiEntry FROM IndexInfo;"));
+ if (sql.prepare() != SQLITE_OK)
+ return nullptr;
+
+ int result = sql.step();
+ while (result == SQLITE_ROW) {
+ uint64_t indexID = sql.getColumnInt64(0);
+ String indexName = sql.getColumnText(1);
+ uint64_t objectStoreID = sql.getColumnInt64(2);
+
+ Vector<char> keyPathBuffer;
+ sql.getColumnBlobAsVector(3, keyPathBuffer);
+
+ std::optional<IDBKeyPath> indexKeyPath;
+ if (!deserializeIDBKeyPath(reinterpret_cast<const uint8_t*>(keyPathBuffer.data()), keyPathBuffer.size(), indexKeyPath)) {
+ LOG_ERROR("Unable to extract key path from database");
+ return nullptr;
+ }
+ if (!indexKeyPath) {
+ LOG_ERROR("Unable to extract key path from database");
+ return nullptr;
+ }
+
+ bool unique = sql.getColumnInt(4);
+ bool multiEntry = sql.getColumnInt(5);
+
+ auto objectStore = databaseInfo->infoForExistingObjectStore(objectStoreID);
+ if (!objectStore) {
+ LOG_ERROR("Found index referring to a non-existant object store");
+ return nullptr;
+ }
+
+ objectStore->addExistingIndex({ indexID, objectStoreID, indexName, WTFMove(indexKeyPath.value()), unique, multiEntry });
+
+ result = sql.step();
+ }
+
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Error fetching index info from database on disk");
+ return nullptr;
+ }
+ }
+
+ return databaseInfo;
+}
+
+String SQLiteIDBBackingStore::databaseNameFromEncodedFilename(const String& encodedName)
+{
+ if (equal(encodedName, ASCIILiteral("%00")))
+ return { };
+
+ String partiallyDecoded = encodedName;
+ partiallyDecoded.replace(ASCIILiteral("%2E"), ASCIILiteral("."));
+
+ return decodeFromFilename(partiallyDecoded);
+}
+
+String SQLiteIDBBackingStore::filenameForDatabaseName() const
+{
+ ASSERT(!m_identifier.databaseName().isNull());
+
+ if (m_identifier.databaseName().isEmpty())
+ return "%00";
+
+ String filename = encodeForFileName(m_identifier.databaseName());
+ filename.replace('.', "%2E");
+
+ return filename;
+}
+
+String SQLiteIDBBackingStore::fullDatabaseDirectory() const
+{
+ ASSERT(!m_identifier.databaseName().isNull());
+
+ return pathByAppendingComponent(m_absoluteDatabaseDirectory, filenameForDatabaseName());
+}
+
+String SQLiteIDBBackingStore::fullDatabasePath() const
+{
+ ASSERT(!m_identifier.databaseName().isNull());
+
+ return pathByAppendingComponent(fullDatabaseDirectory(), "IndexedDB.sqlite3");
+}
+
+IDBError SQLiteIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::getOrEstablishDatabaseInfo - database %s", m_identifier.databaseName().utf8().data());
+
+ if (m_databaseInfo) {
+ info = *m_databaseInfo;
+ return { };
+ }
+
+ makeAllDirectories(fullDatabaseDirectory());
+ String dbFilename = fullDatabasePath();
+
+ m_sqliteDB = std::make_unique<SQLiteDatabase>();
+ if (!m_sqliteDB->open(dbFilename)) {
+ LOG_ERROR("Failed to open SQLite database at path '%s'", dbFilename.utf8().data());
+ closeSQLiteDB();
+ }
+
+ if (!m_sqliteDB)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to open database file on disk") };
+
+ m_sqliteDB->setCollationFunction("IDBKEY", [this](int aLength, const void* a, int bLength, const void* b) {
+ return idbKeyCollate(aLength, a, bLength, b);
+ });
+
+ if (!ensureValidRecordsTable()) {
+ LOG_ERROR("Error creating or migrating Records table in database");
+ closeSQLiteDB();
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Records table in database") };
+ }
+
+ if (!ensureValidIndexRecordsTable()) {
+ LOG_ERROR("Error creating or migrating Index Records table in database");
+ closeSQLiteDB();
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records table in database") };
+ }
+
+ if (!ensureValidIndexRecordsIndex()) {
+ LOG_ERROR("Error creating or migrating Index Records index in database");
+ closeSQLiteDB();
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records index in database") };
+ }
+
+ if (!ensureValidBlobTables()) {
+ LOG_ERROR("Error creating or confirming Blob Records tables in database");
+ closeSQLiteDB();
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or confirming Blob Records tables in database") };
+ }
+
+ auto databaseInfo = extractExistingDatabaseInfo();
+ if (!databaseInfo)
+ databaseInfo = createAndPopulateInitialDatabaseInfo();
+
+ if (!databaseInfo) {
+ LOG_ERROR("Unable to establish IDB database at path '%s'", dbFilename.utf8().data());
+ closeSQLiteDB();
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to establish IDB database file") };
+ }
+
+ m_databaseInfo = WTFMove(databaseInfo);
+ info = *m_databaseInfo;
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::beginTransaction(const IDBTransactionInfo& info)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::beginTransaction - %s", info.identifier().loggingString().utf8().data());
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+ ASSERT(m_databaseInfo);
+
+ auto addResult = m_transactions.add(info.identifier(), nullptr);
+ if (!addResult.isNewEntry) {
+ LOG_ERROR("Attempt to establish transaction identifier that already exists");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to establish transaction identifier that already exists") };
+ }
+
+ addResult.iterator->value = std::make_unique<SQLiteIDBTransaction>(*this, info);
+
+ auto error = addResult.iterator->value->begin(*m_sqliteDB);
+ if (error.isNull() && info.mode() == IDBTransactionMode::Versionchange) {
+ m_originalDatabaseInfoBeforeVersionChange = std::make_unique<IDBDatabaseInfo>(*m_databaseInfo);
+
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("UPDATE IDBDatabaseInfo SET value = ? where key = 'DatabaseVersion';"));
+ if (sql.prepare() != SQLITE_OK
+ || sql.bindText(1, String::number(info.newVersion())) != SQLITE_OK
+ || sql.step() != SQLITE_DONE)
+ error = { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to store new database version in database") };
+ }
+
+ return error;
+}
+
+IDBError SQLiteIDBBackingStore::abortTransaction(const IDBResourceIdentifier& identifier)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::abortTransaction - %s", identifier.loggingString().utf8().data());
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto transaction = m_transactions.take(identifier);
+ if (!transaction) {
+ LOG_ERROR("Attempt to commit a transaction that hasn't been established");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to abort a transaction that hasn't been established") };
+ }
+
+ if (transaction->mode() == IDBTransactionMode::Versionchange && m_originalDatabaseInfoBeforeVersionChange)
+ m_databaseInfo = WTFMove(m_originalDatabaseInfoBeforeVersionChange);
+
+ return transaction->abort();
+}
+
+IDBError SQLiteIDBBackingStore::commitTransaction(const IDBResourceIdentifier& identifier)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::commitTransaction - %s", identifier.loggingString().utf8().data());
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto transaction = m_transactions.take(identifier);
+ if (!transaction) {
+ LOG_ERROR("Attempt to commit a transaction that hasn't been established");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to commit a transaction that hasn't been established") };
+ }
+
+ auto error = transaction->commit();
+ if (!error.isNull()) {
+ if (transaction->mode() == IDBTransactionMode::Versionchange) {
+ ASSERT(m_originalDatabaseInfoBeforeVersionChange);
+ m_databaseInfo = WTFMove(m_originalDatabaseInfoBeforeVersionChange);
+ }
+ } else
+ m_originalDatabaseInfoBeforeVersionChange = nullptr;
+
+ return error;
+}
+
+IDBError SQLiteIDBBackingStore::createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::createObjectStore - adding OS %s with ID %" PRIu64, info.name().utf8().data(), info.identifier());
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to create an object store without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an object store without an in-progress transaction") };
+ }
+ if (transaction->mode() != IDBTransactionMode::Versionchange) {
+ LOG_ERROR("Attempt to create an object store in a non-version-change transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an object store in a non-version-change transaction") };
+ }
+
+ RefPtr<SharedBuffer> keyPathBlob = serializeIDBKeyPath(info.keyPath());
+ if (!keyPathBlob) {
+ LOG_ERROR("Unable to serialize IDBKeyPath to save in database for new object store");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyPath to save in database for new object store") };
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::CreateObjectStoreInfo, ASCIILiteral("INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);"));
+ if (!sql
+ || sql->bindInt64(1, info.identifier()) != SQLITE_OK
+ || sql->bindText(2, info.name()) != SQLITE_OK
+ || sql->bindBlob(3, keyPathBlob->data(), keyPathBlob->size()) != SQLITE_OK
+ || sql->bindInt(4, info.autoIncrement()) != SQLITE_OK
+ || sql->bindInt64(5, info.maxIndexID()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not add object store '%s' to ObjectStoreInfo table (%i) - %s", info.name().utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not create object store") };
+ }
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::CreateObjectStoreKeyGenerator, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, 0);"));
+ if (!sql
+ || sql->bindInt64(1, info.identifier()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not seed initial key generator value for ObjectStoreInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not seed initial key generator value for object store") };
+ }
+ }
+
+ m_databaseInfo->addExistingObjectStore(info);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::deleteObjectStore - object store %" PRIu64, objectStoreIdentifier);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to delete an object store without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete an object store without an in-progress transaction") };
+ }
+ if (transaction->mode() != IDBTransactionMode::Versionchange) {
+ LOG_ERROR("Attempt to delete an object store in a non-version-change transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete an object store in a non-version-change transaction") };
+ }
+
+ // Delete the ObjectStore record
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreInfo, ASCIILiteral("DELETE FROM ObjectStoreInfo WHERE id = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete object store id %" PRIi64 " from ObjectStoreInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete object store") };
+ }
+ }
+
+ // Delete the ObjectStore's key generator record if there is one.
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreKeyGenerator, ASCIILiteral("DELETE FROM KeyGenerators WHERE objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete object store from KeyGenerators table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete key generator for deleted object store") };
+ }
+ }
+
+ // Delete all associated records
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreRecords, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete records for object store %" PRIi64 " (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete records for deleted object store") };
+ }
+ }
+
+ // Delete all associated Indexes
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete index from IndexInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete IDBIndex for deleted object store") };
+ }
+ }
+
+ // Delete all associated Index records
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete index records(%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete IDBIndex records for deleted object store") };
+ }
+ }
+
+ // Delete all unused Blob URL records.
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreBlobRecords, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow NOT IN (SELECT recordID FROM Records)"));
+ if (!sql
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete Blob URL records(%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete stored blob records for deleted object store") };
+ }
+ }
+
+ // Delete all unused Blob File records.
+ auto error = deleteUnusedBlobFileRecords(*transaction);
+ if (!error.isNull())
+ return error;
+
+ m_databaseInfo->deleteObjectStore(objectStoreIdentifier);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::renameObjectStore - object store %" PRIu64, objectStoreIdentifier);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to rename an object store without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an object store without an in-progress transaction") };
+ }
+ if (transaction->mode() != IDBTransactionMode::Versionchange) {
+ LOG_ERROR("Attempt to rename an object store in a non-version-change transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an object store in a non-version-change transaction") };
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::RenameObjectStore, ASCIILiteral("UPDATE ObjectStoreInfo SET name = ? WHERE id = ?;"));
+ if (!sql
+ || sql->bindText(1, newName) != SQLITE_OK
+ || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not update name for object store id %" PRIi64 " in ObjectStoreInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename object store") };
+ }
+ }
+
+ m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::clearObjectStore - object store %" PRIu64, objectStoreID);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to clear an object store without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to clear an object store without an in-progress transaction") };
+ }
+ if (transaction->mode() == IDBTransactionMode::Readonly) {
+ LOG_ERROR("Attempt to clear an object store in a read-only transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to clear an object store in a read-only transaction") };
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::ClearObjectStoreRecords, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not clear records from object store id %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to clear object store") };
+ }
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::ClearObjectStoreIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete records from index record store id %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to delete index records while clearing object store") };
+ }
+ }
+
+ transaction->notifyCursorsOfChanges(objectStoreID);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::createIndex - ObjectStore %" PRIu64 ", Index %" PRIu64, info.objectStoreIdentifier(), info.identifier());
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to create an index without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an index without an in-progress transaction") };
+ }
+ if (transaction->mode() != IDBTransactionMode::Versionchange) {
+ LOG_ERROR("Attempt to create an index in a non-version-change transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to create an index in a non-version-change transaction") };
+ }
+
+ RefPtr<SharedBuffer> keyPathBlob = serializeIDBKeyPath(info.keyPath());
+ if (!keyPathBlob) {
+ LOG_ERROR("Unable to serialize IDBKeyPath to save in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyPath to create index in database") };
+ }
+
+ auto* sql = cachedStatement(SQL::CreateIndexInfo, ASCIILiteral("INSERT INTO IndexInfo VALUES (?, ?, ?, ?, ?, ?);"));
+ if (!sql
+ || sql->bindInt64(1, info.identifier()) != SQLITE_OK
+ || sql->bindText(2, info.name()) != SQLITE_OK
+ || sql->bindInt64(3, info.objectStoreIdentifier()) != SQLITE_OK
+ || sql->bindBlob(4, keyPathBlob->data(), keyPathBlob->size()) != SQLITE_OK
+ || sql->bindInt(5, info.unique()) != SQLITE_OK
+ || sql->bindInt(6, info.multiEntry()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not add index '%s' to IndexInfo table (%i) - %s", info.name().utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to create index in database") };
+ }
+
+ // Write index records for any records that already exist in this object store.
+
+ auto cursor = transaction->maybeOpenBackingStoreCursor(info.objectStoreIdentifier(), 0, IDBKeyRangeData::allKeys());
+
+ if (!cursor) {
+ LOG_ERROR("Cannot open cursor to populate indexes in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to populate indexes in database") };
+ }
+
+ while (!cursor->currentKey().isNull()) {
+ auto& key = cursor->currentKey();
+ auto* value = cursor->currentValue();
+ ThreadSafeDataBuffer valueBuffer = value ? value->data() : ThreadSafeDataBuffer();
+
+ ASSERT(cursor->currentRecordRowID());
+
+ IDBError error = updateOneIndexForAddRecord(info, key, valueBuffer, cursor->currentRecordRowID());
+ if (!error.isNull()) {
+ auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, info.identifier()) != SQLITE_OK
+ || sql->bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database") };
+ }
+
+ return error;
+ }
+
+ if (!cursor->advance(1)) {
+ LOG_ERROR("Error advancing cursor while indexing existing records for new index.");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error advancing cursor while indexing existing records for new index") };
+ }
+ }
+
+ auto* objectStore = m_databaseInfo->infoForExistingObjectStore(info.objectStoreIdentifier());
+ ASSERT(objectStore);
+ objectStore->addExistingIndex(info);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::uncheckedHasIndexRecord(const IDBIndexInfo& info, const IDBKeyData& indexKey, bool& hasRecord)
+{
+ hasRecord = false;
+
+ RefPtr<SharedBuffer> indexKeyBuffer = serializeIDBKeyData(indexKey);
+ if (!indexKeyBuffer) {
+ LOG_ERROR("Unable to serialize index key to be stored in the database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to check for index record in database") };
+ }
+
+ auto* sql = cachedStatement(SQL::HasIndexRecord, ASCIILiteral("SELECT rowid FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key = CAST(? AS TEXT);"));
+ if (!sql
+ || sql->bindInt64(1, info.identifier()) != SQLITE_OK
+ || sql->bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
+ || sql->bindBlob(3, indexKeyBuffer->data(), indexKeyBuffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Error checking for index record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for index record in database") };
+ }
+
+ int sqlResult = sql->step();
+ if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE)
+ return { };
+
+ if (sqlResult != SQLITE_ROW) {
+ // There was an error fetching the record from the database.
+ LOG_ERROR("Could not check if key exists in index (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for existence of IDBKey in index") };
+ }
+
+ hasRecord = true;
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::uncheckedPutIndexKey(const IDBIndexInfo& info, const IDBKeyData& key, const IndexKey& indexKey, int64_t recordID)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedPutIndexKey - (%" PRIu64 ") %s, %s", info.identifier(), key.loggingString().utf8().data(), indexKey.asOneKey().loggingString().utf8().data());
+
+ Vector<IDBKeyData> indexKeys;
+ if (info.multiEntry())
+ indexKeys = indexKey.multiEntry();
+ else
+ indexKeys.append(indexKey.asOneKey());
+
+ if (info.unique()) {
+ bool hasRecord;
+ IDBError error;
+ for (auto& indexKey : indexKeys) {
+ if (!indexKey.isValid())
+ continue;
+ error = uncheckedHasIndexRecord(info, indexKey, hasRecord);
+ if (!error.isNull())
+ return error;
+ if (hasRecord)
+ return IDBError(IDBDatabaseException::ConstraintError);
+ }
+ }
+
+ for (auto& indexKey : indexKeys) {
+ if (!indexKey.isValid())
+ continue;
+ auto error = uncheckedPutIndexRecord(info.objectStoreIdentifier(), info.identifier(), key, indexKey, recordID);
+ if (!error.isNull()) {
+ LOG_ERROR("Unable to put index record for newly created index");
+ return error;
+ }
+ }
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::uncheckedPutIndexRecord(int64_t objectStoreID, int64_t indexID, const WebCore::IDBKeyData& keyValue, const WebCore::IDBKeyData& indexKey, int64_t recordID)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedPutIndexRecord - %s, %s", keyValue.loggingString().utf8().data(), indexKey.loggingString().utf8().data());
+
+ RefPtr<SharedBuffer> indexKeyBuffer = serializeIDBKeyData(indexKey);
+ if (!indexKeyBuffer) {
+ LOG_ERROR("Unable to serialize index key to be stored in the database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize index key to be stored in the database") };
+ }
+
+ RefPtr<SharedBuffer> valueBuffer = serializeIDBKeyData(keyValue);
+ if (!valueBuffer) {
+ LOG_ERROR("Unable to serialize the value to be stored in the database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize value to be stored in the database") };
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::PutIndexRecord, ASCIILiteral("INSERT INTO IndexRecords VALUES (?, ?, CAST(? AS TEXT), CAST(? AS TEXT), ?);"));
+ if (!sql
+ || sql->bindInt64(1, indexID) != SQLITE_OK
+ || sql->bindInt64(2, objectStoreID) != SQLITE_OK
+ || sql->bindBlob(3, indexKeyBuffer->data(), indexKeyBuffer->size()) != SQLITE_OK
+ || sql->bindBlob(4, valueBuffer->data(), valueBuffer->size()) != SQLITE_OK
+ || sql->bindInt64(5, recordID) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not put index record for index %" PRIi64 " in object store %" PRIi64 " in Records table (%i) - %s", indexID, objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error putting index record into database") };
+ }
+ }
+
+ return { };
+}
+
+
+IDBError SQLiteIDBBackingStore::deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::deleteIndex - object store %" PRIu64, objectStoreIdentifier);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to delete index without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index without an in-progress transaction") };
+ }
+
+ if (transaction->mode() != IDBTransactionMode::Versionchange) {
+ LOG_ERROR("Attempt to delete index during a non-version-change transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index during a non-version-change transaction") };
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral("DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, indexIdentifier) != SQLITE_OK
+ || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete index id %" PRIi64 " from IndexInfo table (%i) - %s", objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting index from database") };
+ }
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::DeleteIndexRecords, ASCIILiteral("DELETE FROM IndexRecords WHERE indexID = ? AND objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, indexIdentifier) != SQLITE_OK
+ || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete index records for index id %" PRIi64 " from IndexRecords table (%i) - %s", indexIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting index records from database") };
+ }
+ }
+
+ auto* objectStore = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ ASSERT(objectStore);
+ objectStore->deleteIndex(indexIdentifier);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::renameIndex - object store %" PRIu64 ", index %" PRIu64, objectStoreIdentifier, indexIdentifier);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ if (!objectStoreInfo)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") };
+
+ auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier);
+ if (!indexInfo)
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") };
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to rename an index without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an index without an in-progress transaction") };
+ }
+
+ if (transaction->mode() != IDBTransactionMode::Versionchange) {
+ LOG_ERROR("Attempt to rename an index in a non-version-change transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename an index in a non-version-change transaction") };
+ }
+
+ {
+ auto* sql = cachedStatement(SQL::RenameIndex, ASCIILiteral("UPDATE IndexInfo SET name = ? WHERE objectStoreID = ? AND id = ?;"));
+ if (!sql
+ || sql->bindText(1, newName) != SQLITE_OK
+ || sql->bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+ || sql->bindInt64(3, indexIdentifier) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not update name for index id (%" PRIi64 ", %" PRIi64 ") in IndexInfo table (%i) - %s", objectStoreIdentifier, indexIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not rename index") };
+ }
+ }
+
+ indexInfo->rename(newName);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyData& keyData, bool& keyExists)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::keyExistsInObjectStore - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreID);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ keyExists = false;
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to see if key exists in objectstore without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to see if key exists in objectstore without an in-progress transaction") };
+ }
+
+ RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(keyData);
+ if (!keyBuffer) {
+ LOG_ERROR("Unable to serialize IDBKey to check for existence in object store");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to check for existence in object store") };
+ }
+ auto* sql = cachedStatement(SQL::KeyExistsInObjectStore, ASCIILiteral("SELECT key FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT) LIMIT 1;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to check for existence of IDBKey in object store") };
+ }
+
+ int sqlResult = sql->step();
+ if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE)
+ return { };
+
+ if (sqlResult != SQLITE_ROW) {
+ // There was an error fetching the record from the database.
+ LOG_ERROR("Could not check if key exists in object store (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error checking for existence of IDBKey in object store") };
+ }
+
+ keyExists = true;
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::deleteUnusedBlobFileRecords(SQLiteIDBTransaction& transaction)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::deleteUnusedBlobFileRecords");
+
+ // Gather the set of blob URLs and filenames that are no longer in use.
+ HashSet<String> removedBlobFilenames;
+ {
+ auto* sql = cachedStatement(SQL::GetUnusedBlobFilenames, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)"));
+
+ if (!sql) {
+ LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
+ }
+
+ int result = sql->step();
+ while (result == SQLITE_ROW) {
+ removedBlobFilenames.add(sql->getColumnText(0));
+ result = sql->step();
+ }
+
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
+ }
+ }
+
+ // Remove the blob records that are no longer in use.
+ if (!removedBlobFilenames.isEmpty()) {
+ auto* sql = cachedStatement(SQL::DeleteUnusedBlobs, ASCIILiteral("DELETE FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)"));
+
+ if (!sql
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Error deleting stored blobs (%i) (Could not delete blobFile records) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
+ }
+ }
+
+ for (auto& file : removedBlobFilenames)
+ transaction.addRemovedBlobFile(file);
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::deleteRecord(SQLiteIDBTransaction& transaction, int64_t objectStoreID, const IDBKeyData& keyData)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::deleteRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreID);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+ ASSERT(transaction.inProgress());
+ ASSERT(transaction.mode() != IDBTransactionMode::Readonly);
+ UNUSED_PARAM(transaction);
+
+ RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(keyData);
+ if (!keyBuffer) {
+ LOG_ERROR("Unable to serialize IDBKeyData to be removed from the database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyData to be removed from the database") };
+ }
+
+ // Get the record ID
+ int64_t recordID;
+ {
+ auto* sql = cachedStatement(SQL::GetObjectStoreRecordID, ASCIILiteral("SELECT recordID FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
+
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+ }
+
+ int result = sql->step();
+
+ // If there's no record ID, there's no record to delete.
+ if (result == SQLITE_DONE)
+ return { };
+
+ if (result != SQLITE_ROW) {
+ LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (unable to fetch record ID) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+ }
+
+ recordID = sql->getColumnInt64(0);
+ }
+
+ if (recordID < 1) {
+ LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (record ID is invalid) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+ }
+
+ // Delete the blob records for this object store record.
+ {
+ auto* sql = cachedStatement(SQL::DeleteBlobRecord, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow = ?;"));
+
+ if (!sql
+ || sql->bindInt64(1, recordID) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (Could not delete BlobRecords records) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+ }
+ }
+
+ auto error = deleteUnusedBlobFileRecords(transaction);
+ if (!error.isNull())
+ return error;
+
+ // Delete record from object store
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
+
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+ }
+ }
+
+ // Delete record from indexes store
+ {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);"));
+
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not delete record from indexes for object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete index entries for object store record") };
+ }
+ }
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyRangeData& keyRange)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::deleteRange - range %s, object store %" PRIu64, keyRange.loggingString().utf8().data(), objectStoreID);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to delete range from database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete range from database without an in-progress transaction") };
+ }
+ if (transaction->mode() == IDBTransactionMode::Readonly) {
+ LOG_ERROR("Attempt to delete records from an object store in a read-only transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete records from an object store in a read-only transaction") };
+ }
+
+ // If the range to delete is exactly one key we can delete it right now.
+ if (keyRange.isExactlyOneKey()) {
+ auto error = deleteRecord(*transaction, objectStoreID, keyRange.lowerKey);
+ if (!error.isNull()) {
+ LOG_ERROR("Failed to delete record for key '%s'", keyRange.lowerKey.loggingString().utf8().data());
+ return error;
+ }
+
+ transaction->notifyCursorsOfChanges(objectStoreID);
+
+ return { };
+ }
+
+ auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreID, 0, keyRange);
+ if (!cursor) {
+ LOG_ERROR("Cannot open cursor to delete range of records from the database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to delete range of records from the database") };
+ }
+
+ Vector<IDBKeyData> keys;
+ while (!cursor->didComplete() && !cursor->didError()) {
+ keys.append(cursor->currentKey());
+ cursor->advance(1);
+ }
+
+ if (cursor->didError()) {
+ LOG_ERROR("Cursor failed while accumulating range of records from the database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while accumulating range of records from the database") };
+ }
+
+ IDBError error;
+ for (auto& key : keys) {
+ error = deleteRecord(*transaction, objectStoreID, key);
+ if (!error.isNull()) {
+ LOG_ERROR("deleteRange: Error deleting keys in range");
+ break;
+ }
+ }
+
+ transaction->notifyCursorsOfChanges(objectStoreID);
+
+ return error;
+}
+
+IDBError SQLiteIDBBackingStore::updateOneIndexForAddRecord(const IDBIndexInfo& info, const IDBKeyData& key, const ThreadSafeDataBuffer& value, int64_t recordID)
+{
+ JSLockHolder locker(vm());
+
+ auto jsValue = deserializeIDBValueToJSValue(*globalObject().globalExec(), value);
+ if (jsValue.isUndefinedOrNull())
+ return { };
+
+ IndexKey indexKey;
+ generateIndexKeyForValue(*m_globalObject->globalExec(), info, jsValue, indexKey);
+
+ if (indexKey.isNull())
+ return { };
+
+ return uncheckedPutIndexKey(info, key, indexKey, recordID);
+}
+
+IDBError SQLiteIDBBackingStore::updateAllIndexesForAddRecord(const IDBObjectStoreInfo& info, const IDBKeyData& key, const ThreadSafeDataBuffer& value, int64_t recordID)
+{
+ JSLockHolder locker(vm());
+
+ auto jsValue = deserializeIDBValueToJSValue(*globalObject().globalExec(), value);
+ if (jsValue.isUndefinedOrNull())
+ return { };
+
+ IDBError error;
+ bool anyRecordsSucceeded = false;
+ for (auto& index : info.indexMap().values()) {
+ IndexKey indexKey;
+ generateIndexKeyForValue(*m_globalObject->globalExec(), index, jsValue, indexKey);
+
+ if (indexKey.isNull())
+ continue;
+
+ error = uncheckedPutIndexKey(index, key, indexKey, recordID);
+ if (!error.isNull())
+ break;
+
+ anyRecordsSucceeded = true;
+ }
+
+ if (!error.isNull() && anyRecordsSucceeded) {
+ RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(key);
+
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral("DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);"));
+
+ if (!sql
+ || sql->bindInt64(1, info.identifier()) != SQLITE_OK
+ || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Adding one Index record failed, but failed to remove all others that previously succeeded");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Adding one Index record failed, but failed to remove all others that previously succeeded") };
+ }
+ }
+
+ return error;
+}
+
+IDBError SQLiteIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IDBValue& value)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::addRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreInfo.identifier());
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+ ASSERT(value.data().data());
+ ASSERT(value.blobURLs().size() == value.blobFilePaths().size());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to store a record in an object store without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to store a record in an object store without an in-progress transaction") };
+ }
+ if (transaction->mode() == IDBTransactionMode::Readonly) {
+ LOG_ERROR("Attempt to store a record in an object store in a read-only transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to store a record in an object store in a read-only transaction") };
+ }
+
+ RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(keyData);
+ if (!keyBuffer) {
+ LOG_ERROR("Unable to serialize IDBKey to be stored in an object store");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to be stored in an object store") };
+ }
+
+ int64_t recordID = 0;
+ {
+ auto* sql = cachedStatement(SQL::AddObjectStoreRecord, ASCIILiteral("INSERT INTO Records VALUES (?, CAST(? AS TEXT), ?, NULL);"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
+ || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
+ || sql->bindBlob(3, value.data().data()->data(), value.data().data()->size()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not put record for object store %" PRIi64 " in Records table (%i) - %s", objectStoreInfo.identifier(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to store record in object store") };
+ }
+
+ recordID = m_sqliteDB->lastInsertRowID();
+ }
+
+ auto error = updateAllIndexesForAddRecord(objectStoreInfo, keyData, value.data(), recordID);
+
+ if (!error.isNull()) {
+ auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
+ || sql->bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Indexing new object store record failed, but unable to remove the object store record itself");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Indexing new object store record failed, but unable to remove the object store record itself") };
+ }
+
+ return error;
+ }
+
+ const Vector<String>& blobURLs = value.blobURLs();
+ const Vector<String>& blobFiles = value.blobFilePaths();
+ for (size_t i = 0; i < blobURLs.size(); ++i) {
+ auto& url = blobURLs[i];
+ {
+ auto* sql = cachedStatement(SQL::AddBlobRecord, ASCIILiteral("INSERT INTO BlobRecords VALUES (?, ?);"));
+ if (!sql
+ || sql->bindInt64(1, recordID) != SQLITE_OK
+ || sql->bindText(2, url) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Unable to record Blob record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob record in database") };
+ }
+ }
+ int64_t potentialFileNameInteger = m_sqliteDB->lastInsertRowID();
+
+ // If we already have a file for this blobURL, nothing left to do.
+ {
+ auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL = ?;"));
+ if (!sql
+ || sql->bindText(1, url) != SQLITE_OK) {
+ LOG_ERROR("Unable to examine Blob filenames in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") };
+ }
+
+ int result = sql->step();
+ if (result != SQLITE_ROW && result != SQLITE_DONE) {
+ LOG_ERROR("Unable to examine Blob filenames in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") };
+ }
+
+ if (result == SQLITE_ROW)
+ continue;
+ }
+
+ // We don't already have a file for this blobURL, so commit our file as a unique filename
+ String storedFilename = String::format("%" PRId64 ".blob", potentialFileNameInteger);
+ {
+ auto* sql = cachedStatement(SQL::AddBlobFilename, ASCIILiteral("INSERT INTO BlobFiles VALUES (?, ?);"));
+ if (!sql
+ || sql->bindText(1, url) != SQLITE_OK
+ || sql->bindText(2, storedFilename) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Unable to record Blob file record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob file record in database") };
+ }
+ }
+
+ transaction->addBlobFile(blobFiles[i], storedFilename);
+ }
+
+ transaction->notifyCursorsOfChanges(objectStoreInfo.identifier());
+
+ return error;
+}
+
+IDBError SQLiteIDBBackingStore::getBlobRecordsForObjectStoreRecord(int64_t objectStoreRecord, Vector<String>& blobURLs, Vector<String>& blobFilePaths)
+{
+ ASSERT(objectStoreRecord);
+
+ HashSet<String> blobURLSet;
+ {
+ auto* sql = cachedStatement(SQL::GetBlobURL, ASCIILiteral("SELECT blobURL FROM BlobRecords WHERE objectStoreRow = ?"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreRecord) != SQLITE_OK) {
+ LOG_ERROR("Could not prepare statement to fetch blob URLs for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
+ }
+
+ int sqlResult = sql->step();
+ if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) {
+ // There are no blobURLs in the database for this object store record.
+ return { };
+ }
+
+ while (sqlResult == SQLITE_ROW) {
+ blobURLSet.add(sql->getColumnText(0));
+ sqlResult = sql->step();
+ }
+
+ if (sqlResult != SQLITE_DONE) {
+ LOG_ERROR("Could not fetch blob URLs for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
+ }
+ }
+
+ ASSERT(!blobURLSet.isEmpty());
+ String databaseDirectory = fullDatabaseDirectory();
+ for (auto& blobURL : blobURLSet) {
+ auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL = ?;"));
+ if (!sql
+ || sql->bindText(1, blobURL) != SQLITE_OK) {
+ LOG_ERROR("Could not prepare statement to fetch blob filename for object store record (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
+ }
+
+ if (sql->step() != SQLITE_ROW) {
+ LOG_ERROR("Entry for blob filename for blob url %s does not exist (%i) - %s", blobURL.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up blobURL records in object store by key range") };
+ }
+
+ blobURLs.append(blobURL);
+
+ String fileName = sql->getColumnText(0);
+ blobFilePaths.append(pathByAppendingComponent(databaseDirectory, fileName));
+ }
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, const IDBKeyRangeData& keyRange, IDBGetRecordDataType type, IDBGetResult& resultValue)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::getRecord - key range %s, object store %" PRIu64, keyRange.loggingString().utf8().data(), objectStoreID);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to get a record from database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get a record from database without an in-progress transaction") };
+ }
+
+ auto key = keyRange.lowerKey;
+ if (key.isNull())
+ key = IDBKeyData::minimum();
+ RefPtr<SharedBuffer> lowerBuffer = serializeIDBKeyData(key);
+ if (!lowerBuffer) {
+ LOG_ERROR("Unable to serialize lower IDBKey in lookup range");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize lower IDBKey in lookup range") };
+ }
+
+ key = keyRange.upperKey;
+ if (key.isNull())
+ key = IDBKeyData::maximum();
+ RefPtr<SharedBuffer> upperBuffer = serializeIDBKeyData(key);
+ if (!upperBuffer) {
+ LOG_ERROR("Unable to serialize upper IDBKey in lookup range");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize upper IDBKey in lookup range") };
+ }
+
+ int64_t recordID = 0;
+ ThreadSafeDataBuffer resultBuffer;
+ {
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosedKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+
+ SQLiteStatement* sql = nullptr;
+
+ switch (type) {
+ case IDBGetRecordDataType::KeyAndValue:
+ if (keyRange.lowerOpen) {
+ if (keyRange.upperOpen)
+ sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpen.get());
+ else
+ sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosed.get());
+ } else {
+ if (keyRange.upperOpen)
+ sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpen.get());
+ else
+ sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosed.get());
+ }
+ break;
+ case IDBGetRecordDataType::KeyOnly:
+ if (keyRange.lowerOpen) {
+ if (keyRange.upperOpen)
+ sql = cachedStatement(SQL::GetKeyRecordsLowerOpenUpperOpen, lowerOpenUpperOpenKeyOnly.get());
+ else
+ sql = cachedStatement(SQL::GetKeyRecordsLowerOpenUpperClosed, lowerOpenUpperClosedKeyOnly.get());
+ } else {
+ if (keyRange.upperOpen)
+ sql = cachedStatement(SQL::GetKeyRecordsLowerClosedUpperOpen, lowerClosedUpperOpenKeyOnly.get());
+ else
+ sql = cachedStatement(SQL::GetKeyRecordsLowerClosedUpperClosed, lowerClosedUpperClosedKeyOnly.get());
+ }
+ }
+
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->bindBlob(2, lowerBuffer->data(), lowerBuffer->size()) != SQLITE_OK
+ || sql->bindBlob(3, upperBuffer->data(), upperBuffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Could not get key range record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up record in object store by key range") };
+ }
+
+ int sqlResult = sql->step();
+
+ if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) {
+ // There was no record for the key in the database.
+ return { };
+ }
+ if (sqlResult != SQLITE_ROW) {
+ // There was an error fetching the record from the database.
+ LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error looking up record in object store by key range") };
+ }
+
+ Vector<uint8_t> buffer;
+ sql->getColumnBlobAsVector(0, buffer);
+ resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer);
+
+ if (type == IDBGetRecordDataType::KeyAndValue)
+ recordID = sql->getColumnInt64(1);
+ }
+
+ if (type == IDBGetRecordDataType::KeyOnly) {
+ auto* vector = resultBuffer.data();
+ if (!vector) {
+ LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error extracting key data from database executing IDBObjectStore.getKey()") };
+ }
+
+ IDBKeyData keyData;
+ if (!deserializeIDBKeyData(vector->data(), vector->size(), keyData)) {
+ LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error extracting key data from database executing IDBObjectStore.getKey()") };
+ }
+
+ resultValue = { keyData };
+ return { };
+ }
+
+ ASSERT(recordID);
+ Vector<String> blobURLs, blobFilePaths;
+ auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths);
+ ASSERT(blobURLs.size() == blobFilePaths.size());
+
+ if (!error.isNull())
+ return error;
+
+ resultValue = { { resultBuffer, WTFMove(blobURLs), WTFMove(blobFilePaths) } };
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result)
+{
+ return getAllRecordsData.indexIdentifier ? getAllIndexRecords(transactionIdentifier, getAllRecordsData, result) : getAllObjectStoreRecords(transactionIdentifier, getAllRecordsData, result);
+}
+
+SQLiteStatement* SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords(const IDBGetAllRecordsData& getAllRecordsData)
+{
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosedValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+
+ if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) {
+ if (getAllRecordsData.keyRangeData.lowerOpen) {
+ if (getAllRecordsData.keyRangeData.upperOpen)
+ return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperOpen, lowerOpenUpperOpenKey.get());
+ return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperClosed, lowerOpenUpperClosedKey.get());
+ }
+
+ if (getAllRecordsData.keyRangeData.upperOpen)
+ return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperOpen, lowerClosedUpperOpenKey.get());
+ return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperClosed, lowerClosedUpperClosedKey.get());
+ }
+
+ if (getAllRecordsData.keyRangeData.lowerOpen) {
+ if (getAllRecordsData.keyRangeData.upperOpen)
+ return cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpenValue.get());
+ return cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosedValue.get());
+ }
+
+ if (getAllRecordsData.keyRangeData.upperOpen)
+ return cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpenValue.get());
+ return cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosedValue.get());
+}
+
+IDBError SQLiteIDBBackingStore::getAllObjectStoreRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::getAllObjectStoreRecords");
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to get records from database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get records from database without an in-progress transaction") };
+ }
+
+ auto key = getAllRecordsData.keyRangeData.lowerKey;
+ if (key.isNull())
+ key = IDBKeyData::minimum();
+ auto lowerBuffer = serializeIDBKeyData(key);
+ if (!lowerBuffer) {
+ LOG_ERROR("Unable to serialize lower IDBKey in lookup range");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize lower IDBKey in lookup range") };
+ }
+
+ key = getAllRecordsData.keyRangeData.upperKey;
+ if (key.isNull())
+ key = IDBKeyData::maximum();
+ auto upperBuffer = serializeIDBKeyData(key);
+ if (!upperBuffer) {
+ LOG_ERROR("Unable to serialize upper IDBKey in lookup range");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize upper IDBKey in lookup range") };
+ }
+
+ auto* sql = cachedStatementForGetAllObjectStoreRecords(getAllRecordsData);
+ if (!sql
+ || sql->bindInt64(1, getAllRecordsData.objectStoreIdentifier) != SQLITE_OK
+ || sql->bindBlob(2, lowerBuffer->data(), lowerBuffer->size()) != SQLITE_OK
+ || sql->bindBlob(3, upperBuffer->data(), upperBuffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Could not get key range record from object store %" PRIi64 " from Records table (%i) - %s", getAllRecordsData.objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to look up record in object store by key range") };
+ }
+
+ result = { getAllRecordsData.getAllType };
+
+ uint32_t targetResults;
+ if (getAllRecordsData.count && getAllRecordsData.count.value())
+ targetResults = getAllRecordsData.count.value();
+ else
+ targetResults = std::numeric_limits<uint32_t>::max();
+
+ int sqlResult = sql->step();
+ uint32_t returnedResults = 0;
+
+ while (sqlResult == SQLITE_ROW && returnedResults < targetResults) {
+ if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values) {
+ Vector<uint8_t> buffer;
+ sql->getColumnBlobAsVector(0, buffer);
+ ThreadSafeDataBuffer resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer);
+
+ auto recordID = sql->getColumnInt64(1);
+
+ ASSERT(recordID);
+ Vector<String> blobURLs, blobFilePaths;
+ auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths);
+ ASSERT(blobURLs.size() == blobFilePaths.size());
+
+ if (!error.isNull())
+ return error;
+
+ result.addValue({ resultBuffer, WTFMove(blobURLs), WTFMove(blobFilePaths) });
+ } else {
+ Vector<uint8_t> keyData;
+ IDBKeyData key;
+ sql->getColumnBlobAsVector(0, keyData);
+
+ if (!deserializeIDBKeyData(keyData.data(), keyData.size(), key)) {
+ LOG_ERROR("Unable to deserialize key data from database while getting all key records");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to deserialize key data while getting all key records") };
+ }
+
+ result.addKey(WTFMove(key));
+ }
+
+ ++returnedResults;
+ sqlResult = sql->step();
+ }
+
+ if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE || sqlResult == SQLITE_ROW) {
+ // Finished getting results
+ return { };
+ }
+
+ // There was an error fetching records from the database.
+ LOG_ERROR("Could not get record from object store %" PRIi64 " from Records table (%i) - %s", getAllRecordsData.objectStoreIdentifier, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error looking up record in object store by key range") };
+}
+
+IDBError SQLiteIDBBackingStore::getAllIndexRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData, IDBGetAllResult& result)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::getAllIndexRecords - %s", getAllRecordsData.keyRangeData.loggingString().utf8().data());
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to get all index records from database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get all index records from database without an in-progress transaction") };
+ }
+
+ auto cursor = transaction->maybeOpenBackingStoreCursor(getAllRecordsData.objectStoreIdentifier, getAllRecordsData.indexIdentifier, getAllRecordsData.keyRangeData);
+ if (!cursor) {
+ LOG_ERROR("Cannot open cursor to perform index gets in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to perform index gets in database") };
+ }
+
+ if (cursor->didError()) {
+ LOG_ERROR("Cursor failed while looking up index records in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index records in database") };
+ }
+
+ result = { getAllRecordsData.getAllType };
+ uint32_t currentCount = 0;
+ uint32_t targetCount = getAllRecordsData.count ? getAllRecordsData.count.value() : 0;
+ if (!targetCount)
+ targetCount = std::numeric_limits<uint32_t>::max();
+ while (!cursor->didComplete() && !cursor->didError() && currentCount < targetCount) {
+ if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) {
+ IDBKeyData keyCopy = cursor->currentPrimaryKey();
+ result.addKey(WTFMove(keyCopy));
+ } else
+ result.addValue(cursor->currentValue() ? *cursor->currentValue() : IDBValue());
+
+ ++currentCount;
+ cursor->advance(1);
+ }
+
+ if (cursor->didError()) {
+ LOG_ERROR("Cursor failed while looking up index records in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index records in database") };
+ }
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t indexID, IndexedDB::IndexRecordType type, const IDBKeyRangeData& range, IDBGetResult& getResult)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::getIndexRecord - %s", range.loggingString().utf8().data());
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to get an index record from database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get an index record from database without an in-progress transaction") };
+ }
+
+ if (range.isExactlyOneKey())
+ return uncheckedGetIndexRecordForOneKey(indexID, objectStoreID, type, range.lowerKey, getResult);
+
+ auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreID, indexID, range);
+ if (!cursor) {
+ LOG_ERROR("Cannot open cursor to perform index get in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Cannot open cursor to perform index get in database") };
+ }
+
+ if (cursor->didError()) {
+ LOG_ERROR("Cursor failed while looking up index record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Cursor failed while looking up index record in database") };
+ }
+
+ if (cursor->didComplete())
+ getResult = { };
+ else {
+ if (type == IndexedDB::IndexRecordType::Key)
+ getResult = { cursor->currentPrimaryKey() };
+ else
+ getResult = { cursor->currentValue() ? *cursor->currentValue() : IDBValue(), cursor->currentPrimaryKey() };
+ }
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey(int64_t indexID, int64_t objectStoreID, IndexedDB::IndexRecordType type, const IDBKeyData& key, IDBGetResult& getResult)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey");
+
+ ASSERT(key.isValid() && key.type() != KeyType::Max && key.type() != KeyType::Min);
+
+ RefPtr<SharedBuffer> buffer = serializeIDBKeyData(key);
+ if (!buffer) {
+ LOG_ERROR("Unable to serialize IDBKey to look up one index record");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to look up one index record") };
+ }
+
+ auto* sql = cachedStatement(SQL::GetIndexRecordForOneKey, ASCIILiteral("SELECT IndexRecords.value, Records.value, Records.recordID FROM Records INNER JOIN IndexRecords ON Records.recordID = IndexRecords.objectStoreRecordID WHERE IndexRecords.indexID = ? AND IndexRecords.objectStoreID = ? AND IndexRecords.key = CAST(? AS TEXT) ORDER BY IndexRecords.key, IndexRecords.value"));
+
+ if (!sql
+ || sql->bindInt64(1, indexID) != SQLITE_OK
+ || sql->bindInt64(2, objectStoreID) != SQLITE_OK
+ || sql->bindBlob(3, buffer->data(), buffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Unable to lookup index record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to lookup index record in database") };
+ }
+
+ int result = sql->step();
+ if (result != SQLITE_ROW && result != SQLITE_DONE) {
+ LOG_ERROR("Unable to lookup index record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to lookup index record in database") };
+ }
+
+ if (result == SQLITE_DONE)
+ return { };
+
+ IDBKeyData objectStoreKey;
+ Vector<uint8_t> keyVector;
+ sql->getColumnBlobAsVector(0, keyVector);
+
+ if (!deserializeIDBKeyData(keyVector.data(), keyVector.size(), objectStoreKey)) {
+ LOG_ERROR("Unable to deserialize key looking up index record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to deserialize key looking up index record in database") };
+ }
+
+ if (type == IndexedDB::IndexRecordType::Key) {
+ getResult = { objectStoreKey };
+ return { };
+ }
+
+ sql->getColumnBlobAsVector(1, keyVector);
+
+ int64_t recordID = sql->getColumnInt64(2);
+ Vector<String> blobURLs, blobFilePaths;
+ auto error = getBlobRecordsForObjectStoreRecord(recordID, blobURLs, blobFilePaths);
+ ASSERT(blobURLs.size() == blobFilePaths.size());
+
+ if (!error.isNull())
+ return error;
+
+ getResult = { { ThreadSafeDataBuffer::adoptVector(keyVector), WTFMove(blobURLs), WTFMove(blobFilePaths) }, objectStoreKey };
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, uint64_t& outCount)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::getCount - object store %" PRIu64, objectStoreIdentifier);
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ outCount = 0;
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to get count from database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to get count from database without an in-progress transaction") };
+ }
+
+ auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreIdentifier, indexIdentifier, range);
+ if (!cursor) {
+ LOG_ERROR("Cannot open cursor to populate indexes in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to populate indexes in database") };
+ }
+
+ while (cursor->advance(1))
+ ++outCount;
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::uncheckedGetKeyGeneratorValue(int64_t objectStoreID, uint64_t& outValue)
+{
+ auto* sql = cachedStatement(SQL::GetKeyGeneratorValue, ASCIILiteral("SELECT currentKey FROM KeyGenerators WHERE objectStoreID = ?;"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK) {
+ LOG_ERROR("Could not retrieve currentKey from KeyGenerators table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error getting current key generator value from database") };
+ }
+ int result = sql->step();
+ if (result != SQLITE_ROW) {
+ LOG_ERROR("Could not retreive key generator value for object store, but it should be there.");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error finding current key generator value in database") };
+ }
+
+ int64_t value = sql->getColumnInt64(0);
+ if (value < 0)
+ return { IDBDatabaseException::ConstraintError, "Current key generator value from database is invalid" };
+
+ outValue = value;
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::uncheckedSetKeyGeneratorValue(int64_t objectStoreID, uint64_t value)
+{
+ auto* sql = cachedStatement(SQL::SetKeyGeneratorValue, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, ?);"));
+ if (!sql
+ || sql->bindInt64(1, objectStoreID) != SQLITE_OK
+ || sql->bindInt64(2, value) != SQLITE_OK
+ || sql->step() != SQLITE_DONE) {
+ LOG_ERROR("Could not update key generator value (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return { IDBDatabaseException::ConstraintError, "Error storing new key generator value in database" };
+ }
+
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t& generatedKey)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::generateKeyNumber");
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ // The IndexedDatabase spec defines the max key generator value as 2^53;
+ static uint64_t maxGeneratorValue = 0x20000000000000;
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to generate key in database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to generate key in database without an in-progress transaction") };
+ }
+ if (transaction->mode() == IDBTransactionMode::Readonly) {
+ LOG_ERROR("Attempt to generate key in a read-only transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to generate key in a read-only transaction") };
+ }
+
+ uint64_t currentValue;
+ auto error = uncheckedGetKeyGeneratorValue(objectStoreID, currentValue);
+ if (!error.isNull())
+ return error;
+
+ if (currentValue + 1 > maxGeneratorValue)
+ return { IDBDatabaseException::ConstraintError, "Cannot generate new key value over 2^53 for object store operation" };
+
+ generatedKey = currentValue + 1;
+ return uncheckedSetKeyGeneratorValue(objectStoreID, generatedKey);
+}
+
+IDBError SQLiteIDBBackingStore::revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, uint64_t newKeyNumber)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::revertGeneratedKeyNumber - object store %" PRIu64 ", reverted number %" PRIu64, objectStoreID, newKeyNumber);
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to revert key generator value in database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to revert key generator value in database without an in-progress transaction") };
+ }
+ if (transaction->mode() == IDBTransactionMode::Readonly) {
+ LOG_ERROR("Attempt to revert key generator value in a read-only transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to revert key generator value in a read-only transaction") };
+ }
+
+ ASSERT(newKeyNumber);
+ return uncheckedSetKeyGeneratorValue(objectStoreID, newKeyNumber - 1);
+}
+
+IDBError SQLiteIDBBackingStore::maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID, double newKeyNumber)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::maybeUpdateKeyGeneratorNumber");
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to update key generator value in database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to update key generator value in database without an in-progress transaction") };
+ }
+ if (transaction->mode() == IDBTransactionMode::Readonly) {
+ LOG_ERROR("Attempt to update key generator value in a read-only transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to update key generator value in a read-only transaction") };
+ }
+
+ uint64_t currentValue;
+ auto error = uncheckedGetKeyGeneratorValue(objectStoreID, currentValue);
+ if (!error.isNull())
+ return error;
+
+ if (newKeyNumber <= currentValue)
+ return { };
+
+ uint64_t newKeyInteger(newKeyNumber);
+ if (newKeyInteger <= uint64_t(newKeyNumber))
+ ++newKeyInteger;
+
+ ASSERT(newKeyInteger > uint64_t(newKeyNumber));
+
+ return uncheckedSetKeyGeneratorValue(objectStoreID, newKeyInteger - 1);
+}
+
+IDBError SQLiteIDBBackingStore::openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info, IDBGetResult& result)
+{
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* transaction = m_transactions.get(transactionIdentifier);
+ if (!transaction || !transaction->inProgress()) {
+ LOG_ERROR("Attempt to open a cursor in database without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to open a cursor in database without an in-progress transaction") };
+ }
+
+ auto* cursor = transaction->maybeOpenCursor(info);
+ if (!cursor) {
+ LOG_ERROR("Unable to open cursor");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to open cursor") };
+ }
+
+ m_cursors.set(cursor->identifier(), cursor);
+
+ cursor->currentData(result);
+ return { };
+}
+
+IDBError SQLiteIDBBackingStore::iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data, IDBGetResult& result)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::iterateCursor");
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* cursor = m_cursors.get(cursorIdentifier);
+ if (!cursor) {
+ LOG_ERROR("Attempt to iterate a cursor that doesn't exist");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate a cursor that doesn't exist") };
+ }
+
+ ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier);
+
+ if (!cursor->transaction() || !cursor->transaction()->inProgress()) {
+ LOG_ERROR("Attempt to iterate a cursor without an in-progress transaction");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate a cursor without an in-progress transaction") };
+ }
+
+ auto key = data.keyData;
+ auto primaryKey = data.primaryKeyData;
+ auto count = data.count;
+
+ if (key.isValid()) {
+ if (!cursor->iterate(key, primaryKey)) {
+ LOG_ERROR("Attempt to iterate cursor failed");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to iterate cursor failed") };
+ }
+ } else {
+ ASSERT(!primaryKey.isValid());
+ if (!count)
+ count = 1;
+ if (!cursor->advance(count)) {
+ LOG_ERROR("Attempt to advance cursor failed");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to advance cursor failed") };
+ }
+ }
+
+ cursor->currentData(result);
+ return { };
+}
+
+bool SQLiteIDBBackingStore::prefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier)
+{
+ LOG(IndexedDB, "SQLiteIDBBackingStore::prefetchCursor");
+
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ auto* cursor = m_cursors.get(cursorIdentifier);
+ if (!cursor || !cursor->transaction() || !cursor->transaction()->inProgress())
+ return false;
+
+ ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier);
+
+ return cursor->prefetch();
+}
+
+IDBObjectStoreInfo* SQLiteIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier)
+{
+ ASSERT(m_databaseInfo);
+ return m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+}
+
+void SQLiteIDBBackingStore::deleteBackingStore()
+{
+ String dbFilename = fullDatabasePath();
+
+ LOG(IndexedDB, "SQLiteIDBBackingStore::deleteBackingStore deleting file '%s' on disk", dbFilename.utf8().data());
+
+ Vector<String> blobFiles;
+ {
+ bool errored = true;
+
+ if (m_sqliteDB) {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT fileName FROM BlobFiles;"));
+ if (sql.prepare() == SQLITE_OK) {
+ int result = sql.step();
+ while (result == SQLITE_ROW) {
+ blobFiles.append(sql.getColumnText(0));
+ result = sql.step();
+ }
+
+ if (result == SQLITE_DONE)
+ errored = false;
+ }
+ }
+
+ if (errored)
+ LOG_ERROR("Error getting all blob filenames to be deleted");
+ }
+
+ String databaseDirectory = fullDatabaseDirectory();
+ for (auto& file : blobFiles) {
+ String fullPath = pathByAppendingComponent(databaseDirectory, file);
+ if (!deleteFile(fullPath))
+ LOG_ERROR("Error deleting blob file %s", fullPath.utf8().data());
+ }
+
+ if (m_sqliteDB)
+ closeSQLiteDB();
+
+ SQLiteFileSystem::deleteDatabaseFile(dbFilename);
+ SQLiteFileSystem::deleteEmptyDatabaseDirectory(fullDatabaseDirectory());
+ SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_absoluteDatabaseDirectory);
+}
+
+void SQLiteIDBBackingStore::unregisterCursor(SQLiteIDBCursor& cursor)
+{
+ ASSERT(m_cursors.contains(cursor.identifier()));
+ m_cursors.remove(cursor.identifier());
+}
+
+SQLiteStatement* SQLiteIDBBackingStore::cachedStatement(SQLiteIDBBackingStore::SQL sql, const char* statement)
+{
+ if (sql >= SQL::Count) {
+ LOG_ERROR("Invalid SQL statement ID passed to cachedStatement()");
+ return nullptr;
+ }
+
+ if (m_cachedStatements[static_cast<size_t>(sql)]) {
+ if (m_cachedStatements[static_cast<size_t>(sql)]->reset() == SQLITE_OK)
+ return m_cachedStatements[static_cast<size_t>(sql)].get();
+ m_cachedStatements[static_cast<size_t>(sql)] = nullptr;
+ }
+
+ if (m_sqliteDB) {
+ m_cachedStatements[static_cast<size_t>(sql)] = std::make_unique<SQLiteStatement>(*m_sqliteDB, statement);
+ if (m_cachedStatements[static_cast<size_t>(sql)]->prepare() != SQLITE_OK)
+ m_cachedStatements[static_cast<size_t>(sql)] = nullptr;
+ }
+
+ return m_cachedStatements[static_cast<size_t>(sql)].get();
+}
+
+void SQLiteIDBBackingStore::closeSQLiteDB()
+{
+ for (size_t i = 0; i < static_cast<int>(SQL::Count); ++i)
+ m_cachedStatements[i] = nullptr;
+
+ if (m_sqliteDB)
+ m_sqliteDB->close();
+
+ m_sqliteDB = nullptr;
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h
new file mode 100644
index 000000000..257efe8c1
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBBackingStore.h"
+#include "IDBDatabaseIdentifier.h"
+#include "IDBDatabaseInfo.h"
+#include "IDBResourceIdentifier.h"
+#include "SQLiteIDBTransaction.h"
+#include <JavaScriptCore/Strong.h>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class IndexKey;
+class SQLiteDatabase;
+class SQLiteStatement;
+
+namespace IDBServer {
+
+class SQLiteIDBCursor;
+
+class SQLiteIDBBackingStore : public IDBBackingStore {
+public:
+ SQLiteIDBBackingStore(const IDBDatabaseIdentifier&, const String& databaseRootDirectory, IDBBackingStoreTemporaryFileHandler&);
+
+ ~SQLiteIDBBackingStore() final;
+
+ IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) final;
+
+ IDBError beginTransaction(const IDBTransactionInfo&) final;
+ IDBError abortTransaction(const IDBResourceIdentifier& transactionIdentifier) final;
+ IDBError commitTransaction(const IDBResourceIdentifier& transactionIdentifier) final;
+ IDBError createObjectStore(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&) final;
+ IDBError deleteObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final;
+ IDBError renameObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName) final;
+ IDBError clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier) final;
+ IDBError createIndex(const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&) final;
+ IDBError deleteIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier) final;
+ IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final;
+ IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) final;
+ IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) final;
+ IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) final;
+ IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) final;
+ IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) final;
+ IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) final;
+ IDBError getCount(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, uint64_t& outCount) final;
+ IDBError generateKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t& keyNumber) final;
+ IDBError revertGeneratedKeyNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t keyNumber) final;
+ IDBError maybeUpdateKeyGeneratorNumber(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, double newKeyNumber) final;
+ IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) final;
+ IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) final;
+ bool prefetchCursor(const IDBResourceIdentifier&, const IDBResourceIdentifier&) final;
+
+ IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final;
+ void deleteBackingStore() final;
+
+ bool supportsSimultaneousTransactions() final { return false; }
+ bool isEphemeral() final { return false; }
+
+ void unregisterCursor(SQLiteIDBCursor&);
+
+ String fullDatabaseDirectory() const;
+
+ IDBBackingStoreTemporaryFileHandler& temporaryFileHandler() const { return m_temporaryFileHandler; }
+
+ IDBError getBlobRecordsForObjectStoreRecord(int64_t objectStoreRecord, Vector<String>& blobURLs, Vector<String>& blobFilePaths);
+
+ static String databaseNameFromEncodedFilename(const String&);
+
+private:
+ String filenameForDatabaseName() const;
+ String fullDatabasePath() const;
+
+ bool ensureValidRecordsTable();
+ bool ensureValidIndexRecordsTable();
+ bool ensureValidIndexRecordsIndex();
+ bool ensureValidBlobTables();
+ std::unique_ptr<IDBDatabaseInfo> createAndPopulateInitialDatabaseInfo();
+ std::unique_ptr<IDBDatabaseInfo> extractExistingDatabaseInfo();
+
+ IDBError deleteRecord(SQLiteIDBTransaction&, int64_t objectStoreID, const IDBKeyData&);
+ IDBError uncheckedGetKeyGeneratorValue(int64_t objectStoreID, uint64_t& outValue);
+ IDBError uncheckedSetKeyGeneratorValue(int64_t objectStoreID, uint64_t value);
+
+ IDBError updateAllIndexesForAddRecord(const IDBObjectStoreInfo&, const IDBKeyData&, const ThreadSafeDataBuffer& value, int64_t recordID);
+ IDBError updateOneIndexForAddRecord(const IDBIndexInfo&, const IDBKeyData&, const ThreadSafeDataBuffer& value, int64_t recordID);
+ IDBError uncheckedPutIndexKey(const IDBIndexInfo&, const IDBKeyData& keyValue, const IndexKey&, int64_t recordID);
+ IDBError uncheckedPutIndexRecord(int64_t objectStoreID, int64_t indexID, const IDBKeyData& keyValue, const IDBKeyData& indexKey, int64_t recordID);
+ IDBError uncheckedHasIndexRecord(const IDBIndexInfo&, const IDBKeyData&, bool& hasRecord);
+ IDBError uncheckedGetIndexRecordForOneKey(int64_t indexeID, int64_t objectStoreID, IndexedDB::IndexRecordType, const IDBKeyData&, IDBGetResult&);
+
+ IDBError deleteUnusedBlobFileRecords(SQLiteIDBTransaction&);
+
+ IDBError getAllObjectStoreRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue);
+ IDBError getAllIndexRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue);
+
+ void closeSQLiteDB();
+
+ enum class SQL : size_t {
+ CreateObjectStoreInfo,
+ CreateObjectStoreKeyGenerator,
+ DeleteObjectStoreInfo,
+ DeleteObjectStoreKeyGenerator,
+ DeleteObjectStoreRecords,
+ DeleteObjectStoreIndexInfo,
+ DeleteObjectStoreIndexRecords,
+ DeleteObjectStoreBlobRecords,
+ RenameObjectStore,
+ ClearObjectStoreRecords,
+ ClearObjectStoreIndexRecords,
+ CreateIndexInfo,
+ DeleteIndexInfo,
+ HasIndexRecord,
+ PutIndexRecord,
+ GetIndexRecordForOneKey,
+ DeleteIndexRecords,
+ RenameIndex,
+ KeyExistsInObjectStore,
+ GetUnusedBlobFilenames,
+ DeleteUnusedBlobs,
+ GetObjectStoreRecordID,
+ DeleteBlobRecord,
+ DeleteObjectStoreRecord,
+ DeleteObjectStoreIndexRecord,
+ AddObjectStoreRecord,
+ AddBlobRecord,
+ BlobFilenameForBlobURL,
+ AddBlobFilename,
+ GetBlobURL,
+ GetKeyGeneratorValue,
+ SetKeyGeneratorValue,
+ GetAllKeyRecordsLowerOpenUpperOpen,
+ GetAllKeyRecordsLowerOpenUpperClosed,
+ GetAllKeyRecordsLowerClosedUpperOpen,
+ GetAllKeyRecordsLowerClosedUpperClosed,
+ GetValueRecordsLowerOpenUpperOpen,
+ GetValueRecordsLowerOpenUpperClosed,
+ GetValueRecordsLowerClosedUpperOpen,
+ GetValueRecordsLowerClosedUpperClosed,
+ GetKeyRecordsLowerOpenUpperOpen,
+ GetKeyRecordsLowerOpenUpperClosed,
+ GetKeyRecordsLowerClosedUpperOpen,
+ GetKeyRecordsLowerClosedUpperClosed,
+ Count
+ };
+
+ SQLiteStatement* cachedStatement(SQL, const char*);
+ SQLiteStatement* cachedStatementForGetAllObjectStoreRecords(const IDBGetAllRecordsData&);
+
+ std::unique_ptr<SQLiteStatement> m_cachedStatements[static_cast<int>(SQL::Count)];
+
+ JSC::VM& vm();
+ JSC::JSGlobalObject& globalObject();
+ void initializeVM();
+
+ IDBDatabaseIdentifier m_identifier;
+ std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfoBeforeVersionChange;
+
+ std::unique_ptr<SQLiteDatabase> m_sqliteDB;
+
+ HashMap<IDBResourceIdentifier, std::unique_ptr<SQLiteIDBTransaction>> m_transactions;
+ HashMap<IDBResourceIdentifier, SQLiteIDBCursor*> m_cursors;
+
+ String m_absoluteDatabaseDirectory;
+
+ RefPtr<JSC::VM> m_vm;
+ JSC::Strong<JSC::JSGlobalObject> m_globalObject;
+
+ IDBBackingStoreTemporaryFileHandler& m_temporaryFileHandler;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp
new file mode 100644
index 000000000..0d3e82928
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2014, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "SQLiteIDBCursor.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IDBGetResult.h"
+#include "IDBSerialization.h"
+#include "Logging.h"
+#include "SQLiteIDBBackingStore.h"
+#include "SQLiteIDBTransaction.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include <sqlite3.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+static const size_t prefetchLimit = 8;
+
+std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info)
+{
+ auto cursor = std::make_unique<SQLiteIDBCursor>(transaction, info);
+
+ if (!cursor->establishStatement())
+ return nullptr;
+
+ if (!cursor->advance(1))
+ return nullptr;
+
+ return cursor;
+}
+
+std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreateBackingStoreCursor(SQLiteIDBTransaction& transaction, const uint64_t objectStoreID, const uint64_t indexID, const IDBKeyRangeData& range)
+{
+ auto cursor = std::make_unique<SQLiteIDBCursor>(transaction, objectStoreID, indexID, range);
+
+ if (!cursor->establishStatement())
+ return nullptr;
+
+ if (!cursor->advance(1))
+ return nullptr;
+
+ return cursor;
+}
+
+SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info)
+ : m_transaction(&transaction)
+ , m_cursorIdentifier(info.identifier())
+ , m_objectStoreID(info.objectStoreIdentifier())
+ , m_indexID(info.cursorSource() == IndexedDB::CursorSource::Index ? info.sourceIdentifier() : IDBIndexInfo::InvalidId)
+ , m_cursorDirection(info.cursorDirection())
+ , m_cursorType(info.cursorType())
+ , m_keyRange(info.range())
+{
+ ASSERT(m_objectStoreID);
+}
+
+SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction& transaction, const uint64_t objectStoreID, const uint64_t indexID, const IDBKeyRangeData& range)
+ : m_transaction(&transaction)
+ , m_cursorIdentifier(transaction.transactionIdentifier())
+ , m_objectStoreID(objectStoreID)
+ , m_indexID(indexID ? indexID : IDBIndexInfo::InvalidId)
+ , m_cursorDirection(IndexedDB::CursorDirection::Next)
+ , m_cursorType(IndexedDB::CursorType::KeyAndValue)
+ , m_keyRange(range)
+ , m_backingStoreCursor(true)
+{
+ ASSERT(m_objectStoreID);
+}
+
+SQLiteIDBCursor::~SQLiteIDBCursor()
+{
+ if (m_backingStoreCursor)
+ m_transaction->closeCursor(*this);
+}
+
+void SQLiteIDBCursor::currentData(IDBGetResult& result)
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+
+ auto& currentRecord = m_fetchedRecords.first();
+ if (currentRecord.completed) {
+ ASSERT(!currentRecord.errored);
+ result = { };
+ return;
+ }
+
+ result = { currentRecord.record.key, currentRecord.record.primaryKey, currentRecord.record.value ? *currentRecord.record.value : IDBValue() };
+}
+
+static String buildIndexStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
+{
+ StringBuilder builder;
+
+ builder.appendLiteral("SELECT rowid, key, value FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key ");
+ if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen)
+ builder.appendLiteral(">=");
+ else
+ builder.append('>');
+
+ builder.appendLiteral(" CAST(? AS TEXT) AND key ");
+ if (!keyRange.upperKey.isNull() && !keyRange.upperOpen)
+ builder.appendLiteral("<=");
+ else
+ builder.append('<');
+
+ builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key");
+ if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::Prevunique)
+ builder.appendLiteral(" DESC");
+
+ builder.appendLiteral(", value");
+ if (cursorDirection == IndexedDB::CursorDirection::Prev)
+ builder.appendLiteral(" DESC");
+
+ builder.append(';');
+
+ return builder.toString();
+}
+
+static String buildObjectStoreStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
+{
+ StringBuilder builder;
+
+ builder.appendLiteral("SELECT rowid, key, value FROM Records WHERE objectStoreID = ? AND key ");
+
+ if (!keyRange.lowerKey.isNull() && !keyRange.lowerOpen)
+ builder.appendLiteral(">=");
+ else
+ builder.append('>');
+
+ builder.appendLiteral(" CAST(? AS TEXT) AND key ");
+
+ if (!keyRange.upperKey.isNull() && !keyRange.upperOpen)
+ builder.appendLiteral("<=");
+ else
+ builder.append('<');
+
+ builder.appendLiteral(" CAST(? AS TEXT) ORDER BY key");
+
+ if (cursorDirection == IndexedDB::CursorDirection::Prev || cursorDirection == IndexedDB::CursorDirection::Prevunique)
+ builder.appendLiteral(" DESC");
+
+ builder.append(';');
+
+ return builder.toString();
+}
+
+bool SQLiteIDBCursor::establishStatement()
+{
+ ASSERT(!m_statement);
+ String sql;
+
+ if (m_indexID != IDBIndexInfo::InvalidId) {
+ sql = buildIndexStatement(m_keyRange, m_cursorDirection);
+ m_boundID = m_indexID;
+ } else {
+ sql = buildObjectStoreStatement(m_keyRange, m_cursorDirection);
+ m_boundID = m_objectStoreID;
+ }
+
+ m_currentLowerKey = m_keyRange.lowerKey.isNull() ? IDBKeyData::minimum() : m_keyRange.lowerKey;
+ m_currentUpperKey = m_keyRange.upperKey.isNull() ? IDBKeyData::maximum() : m_keyRange.upperKey;
+
+ return createSQLiteStatement(sql);
+}
+
+bool SQLiteIDBCursor::createSQLiteStatement(const String& sql)
+{
+ LOG(IndexedDB, "Creating cursor with SQL query: \"%s\"", sql.utf8().data());
+
+ ASSERT(!m_currentLowerKey.isNull());
+ ASSERT(!m_currentUpperKey.isNull());
+ ASSERT(m_transaction->sqliteTransaction());
+
+ m_statement = std::make_unique<SQLiteStatement>(m_transaction->sqliteTransaction()->database(), sql);
+
+ if (m_statement->prepare() != SQLITE_OK) {
+ LOG_ERROR("Could not create cursor statement (prepare/id) - '%s'", m_transaction->sqliteTransaction()->database().lastErrorMsg());
+ return false;
+ }
+
+ return bindArguments();
+}
+
+void SQLiteIDBCursor::objectStoreRecordsChanged()
+{
+ if (m_statementNeedsReset)
+ return;
+
+ // If ObjectStore or Index contents changed, we need to reset the statement and bind new parameters to it.
+ // This is to pick up any changes that might exist.
+ // We also need to throw away any fetched records as they may no longer be valid.
+
+ m_statementNeedsReset = true;
+ ASSERT(!m_fetchedRecords.isEmpty());
+
+ if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) {
+ m_currentLowerKey = m_fetchedRecords.first().record.key;
+ if (!m_keyRange.lowerOpen) {
+ m_keyRange.lowerOpen = true;
+ m_keyRange.lowerKey = m_currentLowerKey;
+ m_statement = nullptr;
+ }
+ } else {
+ m_currentUpperKey = m_fetchedRecords.first().record.key;
+ if (!m_keyRange.upperOpen) {
+ m_keyRange.upperOpen = true;
+ m_keyRange.upperKey = m_currentUpperKey;
+ m_statement = nullptr;
+ }
+ }
+
+ m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
+
+ m_fetchedRecords.clear();
+}
+
+void SQLiteIDBCursor::resetAndRebindStatement()
+{
+ ASSERT(!m_currentLowerKey.isNull());
+ ASSERT(!m_currentUpperKey.isNull());
+ ASSERT(m_transaction->sqliteTransaction());
+ ASSERT(m_statementNeedsReset);
+
+ m_statementNeedsReset = false;
+
+ if (!m_statement && !establishStatement()) {
+ LOG_ERROR("Unable to establish new statement for cursor iteration");
+ return;
+ }
+
+ if (m_statement->reset() != SQLITE_OK) {
+ LOG_ERROR("Could not reset cursor statement to respond to object store changes");
+ return;
+ }
+
+ bindArguments();
+}
+
+bool SQLiteIDBCursor::bindArguments()
+{
+ LOG(IndexedDB, "Cursor is binding lower key '%s' and upper key '%s'", m_currentLowerKey.loggingString().utf8().data(), m_currentUpperKey.loggingString().utf8().data());
+
+ int currentBindArgument = 1;
+
+ if (m_statement->bindInt64(currentBindArgument++, m_boundID) != SQLITE_OK) {
+ LOG_ERROR("Could not bind id argument (bound ID)");
+ return false;
+ }
+
+ if (m_indexID != IDBIndexInfo::InvalidId && m_statement->bindInt64(currentBindArgument++, m_objectStoreID) != SQLITE_OK) {
+ LOG_ERROR("Could not bind object store id argument for an index cursor");
+ return false;
+ }
+
+ RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_currentLowerKey);
+ if (m_statement->bindBlob(currentBindArgument++, buffer->data(), buffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Could not create cursor statement (lower key)");
+ return false;
+ }
+
+ buffer = serializeIDBKeyData(m_currentUpperKey);
+ if (m_statement->bindBlob(currentBindArgument++, buffer->data(), buffer->size()) != SQLITE_OK) {
+ LOG_ERROR("Could not create cursor statement (upper key)");
+ return false;
+ }
+
+ return true;
+}
+
+bool SQLiteIDBCursor::prefetch()
+{
+ LOG(IndexedDB, "SQLiteIDBCursor::prefetch() - Cursor already has %zu fetched records", m_fetchedRecords.size());
+
+ if (m_fetchedRecords.isEmpty() || m_fetchedRecords.size() >= prefetchLimit || m_fetchedRecords.last().isTerminalRecord())
+ return false;
+
+ m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
+ fetch();
+
+ return m_fetchedRecords.size() < prefetchLimit;
+}
+
+bool SQLiteIDBCursor::advance(uint64_t count)
+{
+ LOG(IndexedDB, "SQLiteIDBCursor::advance() - Count %" PRIu64 ", %zu fetched records", count, m_fetchedRecords.size());
+ ASSERT(count);
+
+ if (!m_fetchedRecords.isEmpty() && m_fetchedRecords.first().isTerminalRecord()) {
+ LOG_ERROR("Attempt to advance a completed cursor");
+ return false;
+ }
+
+ if (!m_fetchedRecords.isEmpty())
+ m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
+
+ // Drop already-fetched records up to `count` to see if we've already fetched the record we're looking for.
+ bool hadCurrentRecord = !m_fetchedRecords.isEmpty();
+ for (; count && !m_fetchedRecords.isEmpty(); --count) {
+ if (m_fetchedRecords.first().isTerminalRecord())
+ break;
+
+ m_fetchedRecords.removeFirst();
+ }
+
+ // If we still have any records left, the first record is our new current record.
+ if (!m_fetchedRecords.isEmpty())
+ return true;
+
+ ASSERT(m_fetchedRecords.isEmpty());
+
+ // If we started out with a current record, we burnt a count on removing it.
+ // Replace that count now.
+ if (hadCurrentRecord)
+ ++count;
+
+ for (; count; --count) {
+ if (!m_fetchedRecords.isEmpty()) {
+ ASSERT(m_fetchedRecords.size() == 1);
+ m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
+ m_fetchedRecords.removeFirst();
+ }
+
+ if (!fetch())
+ return false;
+
+ ASSERT(!m_fetchedRecords.isEmpty());
+ ASSERT(!m_fetchedRecords.first().errored);
+ if (m_fetchedRecords.first().completed)
+ break;
+ }
+
+ return true;
+}
+
+bool SQLiteIDBCursor::fetch()
+{
+ ASSERT(m_fetchedRecords.isEmpty() || !m_fetchedRecords.last().isTerminalRecord());
+
+ m_fetchedRecords.append({ });
+
+ bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::Nextunique || m_cursorDirection == IndexedDB::CursorDirection::Prevunique;
+ if (!isUnique)
+ return fetchNextRecord(m_fetchedRecords.last());
+
+ while (!m_fetchedRecords.last().completed) {
+ if (!fetchNextRecord(m_fetchedRecords.last()))
+ return false;
+
+ // If the new current key is different from the old current key, we're done.
+ if (m_currentKeyForUniqueness.compare(m_fetchedRecords.last().record.key))
+ return true;
+ }
+
+ return false;
+}
+
+bool SQLiteIDBCursor::fetchNextRecord(SQLiteCursorRecord& record)
+{
+ if (m_statementNeedsReset)
+ resetAndRebindStatement();
+
+ FetchResult result;
+ do {
+ result = internalFetchNextRecord(record);
+ } while (result == FetchResult::ShouldFetchAgain);
+
+ return result == FetchResult::Success;
+}
+
+void SQLiteIDBCursor::markAsErrored(SQLiteCursorRecord& record)
+{
+ record.record = { };
+ record.completed = true;
+ record.errored = true;
+ record.rowID = 0;
+}
+
+SQLiteIDBCursor::FetchResult SQLiteIDBCursor::internalFetchNextRecord(SQLiteCursorRecord& record)
+{
+ ASSERT(m_transaction->sqliteTransaction());
+ ASSERT(m_statement);
+ ASSERT(!m_fetchedRecords.isEmpty());
+ ASSERT(!m_fetchedRecords.last().isTerminalRecord());
+
+ record.record.value = nullptr;
+
+ int result = m_statement->step();
+ if (result == SQLITE_DONE) {
+ // When a cursor reaches its end, that is indicated by having undefined keys/values
+ record = { };
+ record.completed = true;
+
+ return FetchResult::Success;
+ }
+
+ if (result != SQLITE_ROW) {
+ LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg());
+ markAsErrored(record);
+ return FetchResult::Failure;
+ }
+
+ record.rowID = m_statement->getColumnInt64(0);
+ ASSERT(record.rowID);
+
+ Vector<uint8_t> keyData;
+ m_statement->getColumnBlobAsVector(1, keyData);
+
+ if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.key)) {
+ LOG_ERROR("Unable to deserialize key data from database while advancing cursor");
+ markAsErrored(record);
+ return FetchResult::Failure;
+ }
+
+ m_statement->getColumnBlobAsVector(2, keyData);
+
+ // The primaryKey of an ObjectStore cursor is the same as its key.
+ if (m_indexID == IDBIndexInfo::InvalidId) {
+ record.record.primaryKey = record.record.key;
+
+ Vector<String> blobURLs, blobFilePaths;
+ auto error = m_transaction->backingStore().getBlobRecordsForObjectStoreRecord(record.rowID, blobURLs, blobFilePaths);
+ if (!error.isNull()) {
+ LOG_ERROR("Unable to fetch blob records from database while advancing cursor");
+ markAsErrored(record);
+ return FetchResult::Failure;
+ }
+
+ if (m_cursorType == IndexedDB::CursorType::KeyAndValue)
+ record.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::adoptVector(keyData), blobURLs, blobFilePaths);
+ } else {
+ if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.primaryKey)) {
+ LOG_ERROR("Unable to deserialize value data from database while advancing index cursor");
+ markAsErrored(record);
+ return FetchResult::Failure;
+ }
+
+ SQLiteStatement objectStoreStatement(m_statement->database(), "SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;");
+
+ if (objectStoreStatement.prepare() != SQLITE_OK
+ || objectStoreStatement.bindBlob(1, keyData.data(), keyData.size()) != SQLITE_OK
+ || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLITE_OK) {
+ LOG_ERROR("Could not create index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
+ markAsErrored(record);
+ return FetchResult::Failure;
+ }
+
+ int result = objectStoreStatement.step();
+
+ if (result == SQLITE_ROW) {
+ objectStoreStatement.getColumnBlobAsVector(0, keyData);
+ record.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::adoptVector(keyData));
+ } else if (result == SQLITE_DONE) {
+ // This indicates that the record we're trying to retrieve has been removed from the object store.
+ // Skip over it.
+ return FetchResult::ShouldFetchAgain;
+ } else {
+ LOG_ERROR("Could not step index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
+ markAsErrored(record);
+ return FetchResult::Failure;
+
+ }
+ }
+
+ return FetchResult::Success;
+}
+
+bool SQLiteIDBCursor::iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey)
+{
+ ASSERT(m_transaction->sqliteTransaction());
+ ASSERT(m_statement);
+
+ bool result = advance(1);
+ ASSERT(!m_fetchedRecords.isEmpty());
+
+ // Iterating with no key is equivalent to advancing 1 step.
+ if (targetKey.isNull() || !result)
+ return result;
+
+ while (!m_fetchedRecords.first().isTerminalRecord()) {
+ if (!result)
+ return false;
+
+ // Search for the next key >= the target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor.
+ if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) {
+ if (m_fetchedRecords.first().record.key.compare(targetKey) >= 0)
+ break;
+ } else if (m_fetchedRecords.first().record.key.compare(targetKey) <= 0)
+ break;
+
+ result = advance(1);
+ }
+
+ if (targetPrimaryKey.isValid()) {
+ while (!m_fetchedRecords.first().isTerminalRecord() && !m_fetchedRecords.first().record.key.compare(targetKey)) {
+ if (!result)
+ return false;
+
+ // Search for the next primary key >= the primary target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor.
+ if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::Nextunique) {
+ if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) >= 0)
+ break;
+ } else if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) <= 0)
+ break;
+
+ result = advance(1);
+ }
+ }
+
+ return result;
+}
+
+const IDBKeyData& SQLiteIDBCursor::currentKey() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().record.key;
+}
+
+const IDBKeyData& SQLiteIDBCursor::currentPrimaryKey() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().record.primaryKey;
+}
+
+IDBValue* SQLiteIDBCursor::currentValue() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().record.value.get();
+}
+
+bool SQLiteIDBCursor::didComplete() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().completed;
+}
+
+bool SQLiteIDBCursor::didError() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().errored;
+}
+
+int64_t SQLiteIDBCursor::currentRecordRowID() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().rowID;
+}
+
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h
new file mode 100644
index 000000000..4a673003b
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorRecord.h"
+#include "IDBIndexInfo.h"
+#include "IDBKeyData.h"
+#include "IDBKeyRangeData.h"
+#include "IDBResourceIdentifier.h"
+#include "IDBValue.h"
+#include "SQLiteStatement.h"
+#include <wtf/Deque.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBGetResult;
+
+namespace IDBServer {
+
+class SQLiteIDBTransaction;
+
+class SQLiteIDBCursor {
+ WTF_MAKE_NONCOPYABLE(SQLiteIDBCursor);
+public:
+ static std::unique_ptr<SQLiteIDBCursor> maybeCreate(SQLiteIDBTransaction&, const IDBCursorInfo&);
+ static std::unique_ptr<SQLiteIDBCursor> maybeCreateBackingStoreCursor(SQLiteIDBTransaction&, const uint64_t objectStoreIdentifier, const uint64_t indexIdentifier, const IDBKeyRangeData&);
+
+ SQLiteIDBCursor(SQLiteIDBTransaction&, const IDBCursorInfo&);
+ SQLiteIDBCursor(SQLiteIDBTransaction&, uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData&);
+
+ ~SQLiteIDBCursor();
+
+ const IDBResourceIdentifier& identifier() const { return m_cursorIdentifier; }
+ SQLiteIDBTransaction* transaction() const { return m_transaction; }
+
+ int64_t objectStoreID() const { return m_objectStoreID; }
+ int64_t currentRecordRowID() const;
+
+ const IDBKeyData& currentKey() const;
+ const IDBKeyData& currentPrimaryKey() const;
+ IDBValue* currentValue() const;
+
+ bool advance(uint64_t count);
+ bool iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey);
+ bool prefetch();
+
+ bool didComplete() const;
+ bool didError() const;
+
+ void objectStoreRecordsChanged();
+
+ void currentData(IDBGetResult&);
+
+private:
+ bool establishStatement();
+ bool createSQLiteStatement(const String& sql);
+ bool bindArguments();
+
+ void resetAndRebindStatement();
+
+ enum class FetchResult {
+ Success,
+ Failure,
+ ShouldFetchAgain
+ };
+
+ bool fetch();
+
+ struct SQLiteCursorRecord {
+ IDBCursorRecord record;
+ bool completed { false };
+ bool errored { false };
+ int64_t rowID { 0 };
+ bool isTerminalRecord() const { return completed || errored; }
+ };
+ bool fetchNextRecord(SQLiteCursorRecord&);
+ FetchResult internalFetchNextRecord(SQLiteCursorRecord&);
+
+ void markAsErrored(SQLiteCursorRecord&);
+
+ SQLiteIDBTransaction* m_transaction;
+ IDBResourceIdentifier m_cursorIdentifier;
+ int64_t m_objectStoreID;
+ int64_t m_indexID { IDBIndexInfo::InvalidId };
+ IndexedDB::CursorDirection m_cursorDirection { IndexedDB::CursorDirection::Next };
+ IndexedDB::CursorType m_cursorType;
+ IDBKeyRangeData m_keyRange;
+
+ IDBKeyData m_currentLowerKey;
+ IDBKeyData m_currentUpperKey;
+
+ Deque<SQLiteCursorRecord> m_fetchedRecords;
+ IDBKeyData m_currentKeyForUniqueness;
+
+ std::unique_ptr<SQLiteStatement> m_statement;
+ bool m_statementNeedsReset { true };
+ int64_t m_boundID { 0 };
+
+ bool m_backingStoreCursor { false };
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp
new file mode 100644
index 000000000..fc1dd17dd
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2013, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "SQLiteIDBTransaction.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "FileSystem.h"
+#include "IDBCursorInfo.h"
+#include "IndexedDB.h"
+#include "Logging.h"
+#include "SQLiteIDBBackingStore.h"
+#include "SQLiteIDBCursor.h"
+#include "SQLiteTransaction.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+SQLiteIDBTransaction::SQLiteIDBTransaction(SQLiteIDBBackingStore& backingStore, const IDBTransactionInfo& info)
+ : m_info(info)
+ , m_backingStore(backingStore)
+{
+}
+
+SQLiteIDBTransaction::~SQLiteIDBTransaction()
+{
+ if (inProgress())
+ m_sqliteTransaction->rollback();
+
+ // Explicitly clear cursors, as that also unregisters them from the backing store.
+ clearCursors();
+}
+
+
+IDBError SQLiteIDBTransaction::begin(SQLiteDatabase& database)
+{
+ ASSERT(!m_sqliteTransaction);
+
+ m_sqliteTransaction = std::make_unique<SQLiteTransaction>(database, m_info.mode() == IDBTransactionMode::Readonly);
+ m_sqliteTransaction->begin();
+
+ if (m_sqliteTransaction->inProgress())
+ return { };
+
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not start SQLite transaction in database backend") };
+}
+
+IDBError SQLiteIDBTransaction::commit()
+{
+ LOG(IndexedDB, "SQLiteIDBTransaction::commit");
+ if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to commit") };
+
+ m_sqliteTransaction->commit();
+
+ if (m_sqliteTransaction->inProgress())
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to commit SQLite transaction in database backend") };
+
+ deleteBlobFilesIfNecessary();
+ moveBlobFilesIfNecessary();
+
+ reset();
+ return { };
+}
+
+void SQLiteIDBTransaction::moveBlobFilesIfNecessary()
+{
+ String databaseDirectory = m_backingStore.fullDatabaseDirectory();
+ for (auto& entry : m_blobTemporaryAndStoredFilenames) {
+ m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first);
+
+ if (!hardLinkOrCopyFile(entry.first, pathByAppendingComponent(databaseDirectory, entry.second)))
+ LOG_ERROR("Failed to link/copy temporary blob file '%s' to location '%s'", entry.first.utf8().data(), pathByAppendingComponent(databaseDirectory, entry.second).utf8().data());
+
+ m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first);
+ }
+
+ m_blobTemporaryAndStoredFilenames.clear();
+}
+
+void SQLiteIDBTransaction::deleteBlobFilesIfNecessary()
+{
+ if (m_blobRemovedFilenames.isEmpty())
+ return;
+
+ String databaseDirectory = m_backingStore.fullDatabaseDirectory();
+ for (auto& entry : m_blobRemovedFilenames) {
+ String fullPath = pathByAppendingComponent(databaseDirectory, entry);
+ m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(fullPath);
+ m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(fullPath);
+ }
+
+ m_blobRemovedFilenames.clear();
+}
+
+IDBError SQLiteIDBTransaction::abort()
+{
+ for (auto& entry : m_blobTemporaryAndStoredFilenames) {
+ m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first);
+ m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first);
+ }
+
+ m_blobTemporaryAndStoredFilenames.clear();
+
+ if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to abort") };
+
+ m_sqliteTransaction->rollback();
+
+ if (m_sqliteTransaction->inProgress())
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to abort SQLite transaction in database backend") };
+
+ reset();
+ return { };
+}
+
+void SQLiteIDBTransaction::reset()
+{
+ m_sqliteTransaction = nullptr;
+ clearCursors();
+ ASSERT(m_blobTemporaryAndStoredFilenames.isEmpty());
+}
+
+std::unique_ptr<SQLiteIDBCursor> SQLiteIDBTransaction::maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData& range)
+{
+ ASSERT(m_sqliteTransaction);
+ ASSERT(m_sqliteTransaction->inProgress());
+
+ auto cursor = SQLiteIDBCursor::maybeCreateBackingStoreCursor(*this, objectStoreID, indexID, range);
+
+ if (cursor)
+ m_backingStoreCursors.add(cursor.get());
+
+ return cursor;
+}
+
+SQLiteIDBCursor* SQLiteIDBTransaction::maybeOpenCursor(const IDBCursorInfo& info)
+{
+ ASSERT(m_sqliteTransaction);
+ if (!m_sqliteTransaction->inProgress())
+ return nullptr;
+
+ auto addResult = m_cursors.add(info.identifier(), SQLiteIDBCursor::maybeCreate(*this, info));
+
+ ASSERT(addResult.isNewEntry);
+
+ // It is possible the cursor failed to create and we just stored a null value.
+ if (!addResult.iterator->value) {
+ m_cursors.remove(addResult.iterator);
+ return nullptr;
+ }
+
+ return addResult.iterator->value.get();
+}
+
+void SQLiteIDBTransaction::closeCursor(SQLiteIDBCursor& cursor)
+{
+ auto backingStoreTake = m_backingStoreCursors.take(&cursor);
+ if (backingStoreTake) {
+ ASSERT(!m_cursors.contains(cursor.identifier()));
+ return;
+ }
+
+ ASSERT(m_cursors.contains(cursor.identifier()));
+
+ m_backingStore.unregisterCursor(cursor);
+ m_cursors.remove(cursor.identifier());
+}
+
+void SQLiteIDBTransaction::notifyCursorsOfChanges(int64_t objectStoreID)
+{
+ for (auto& i : m_cursors) {
+ if (i.value->objectStoreID() == objectStoreID)
+ i.value->objectStoreRecordsChanged();
+ }
+
+ for (auto* cursor : m_backingStoreCursors) {
+ if (cursor->objectStoreID() == objectStoreID)
+ cursor->objectStoreRecordsChanged();
+ }
+}
+
+void SQLiteIDBTransaction::clearCursors()
+{
+ for (auto& cursor : m_cursors.values())
+ m_backingStore.unregisterCursor(*cursor);
+
+ m_cursors.clear();
+}
+
+bool SQLiteIDBTransaction::inProgress() const
+{
+ return m_sqliteTransaction && m_sqliteTransaction->inProgress();
+}
+
+void SQLiteIDBTransaction::addBlobFile(const String& temporaryPath, const String& storedFilename)
+{
+ m_blobTemporaryAndStoredFilenames.append({ temporaryPath, storedFilename });
+}
+
+void SQLiteIDBTransaction::addRemovedBlobFile(const String& removedFilename)
+{
+ ASSERT(!m_blobRemovedFilenames.contains(removedFilename));
+ m_blobRemovedFilenames.add(removedFilename);
+}
+
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h
new file mode 100644
index 000000000..a2753d6c7
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBError.h"
+#include "IDBResourceIdentifier.h"
+#include "IDBTransactionInfo.h"
+#include "IndexedDB.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class SQLiteDatabase;
+class SQLiteTransaction;
+struct IDBKeyRangeData;
+
+namespace IDBServer {
+
+class SQLiteIDBBackingStore;
+class SQLiteIDBCursor;
+
+class SQLiteIDBTransaction {
+ WTF_MAKE_NONCOPYABLE(SQLiteIDBTransaction);
+public:
+ SQLiteIDBTransaction(SQLiteIDBBackingStore&, const IDBTransactionInfo&);
+ ~SQLiteIDBTransaction();
+
+ const IDBResourceIdentifier& transactionIdentifier() const { return m_info.identifier(); }
+
+ IDBError begin(SQLiteDatabase&);
+ IDBError commit();
+ IDBError abort();
+
+ std::unique_ptr<SQLiteIDBCursor> maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData&);
+ SQLiteIDBCursor* maybeOpenCursor(const IDBCursorInfo&);
+
+ void closeCursor(SQLiteIDBCursor&);
+ void notifyCursorsOfChanges(int64_t objectStoreID);
+
+ IDBTransactionMode mode() const { return m_info.mode(); }
+ bool inProgress() const;
+
+ SQLiteTransaction* sqliteTransaction() const { return m_sqliteTransaction.get(); }
+ SQLiteIDBBackingStore& backingStore() { return m_backingStore; }
+
+ void addBlobFile(const String& temporaryPath, const String& storedFilename);
+ void addRemovedBlobFile(const String& removedFilename);
+
+private:
+ void clearCursors();
+ void reset();
+
+ void moveBlobFilesIfNecessary();
+ void deleteBlobFilesIfNecessary();
+
+ IDBTransactionInfo m_info;
+
+ SQLiteIDBBackingStore& m_backingStore;
+ std::unique_ptr<SQLiteTransaction> m_sqliteTransaction;
+ HashMap<IDBResourceIdentifier, std::unique_ptr<SQLiteIDBCursor>> m_cursors;
+ HashSet<SQLiteIDBCursor*> m_backingStoreCursors;
+ Vector<std::pair<String, String>> m_blobTemporaryAndStoredFilenames;
+ HashSet<String> m_blobRemovedFilenames;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp
new file mode 100644
index 000000000..403b4d4a0
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "ServerOpenDBRequest.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBResultData.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<ServerOpenDBRequest> ServerOpenDBRequest::create(IDBConnectionToClient& connection, const IDBRequestData& requestData)
+{
+ return adoptRef(*new ServerOpenDBRequest(connection, requestData));
+}
+
+ServerOpenDBRequest::ServerOpenDBRequest(IDBConnectionToClient& connection, const IDBRequestData& requestData)
+ : m_connection(connection)
+ , m_requestData(requestData)
+{
+}
+
+bool ServerOpenDBRequest::isOpenRequest() const
+{
+ return m_requestData.isOpenRequest();
+}
+
+bool ServerOpenDBRequest::isDeleteRequest() const
+{
+ return m_requestData.isDeleteRequest();
+}
+
+void ServerOpenDBRequest::maybeNotifyRequestBlocked(uint64_t currentVersion)
+{
+ if (m_notifiedBlocked)
+ return;
+
+ uint64_t requestedVersion = isOpenRequest() ? m_requestData.requestedVersion() : 0;
+ m_connection.notifyOpenDBRequestBlocked(m_requestData.requestIdentifier(), currentVersion, requestedVersion);
+
+ m_notifiedBlocked = true;
+}
+
+void ServerOpenDBRequest::notifyDidDeleteDatabase(const IDBDatabaseInfo& info)
+{
+ ASSERT(isDeleteRequest());
+
+ m_connection.didDeleteDatabase(IDBResultData::deleteDatabaseSuccess(m_requestData.requestIdentifier(), info));
+}
+
+void ServerOpenDBRequest::notifiedConnectionsOfVersionChange(HashSet<uint64_t>&& connectionIdentifiers)
+{
+ ASSERT(!m_notifiedConnectionsOfVersionChange);
+
+ m_notifiedConnectionsOfVersionChange = true;
+ m_connectionsPendingVersionChangeEvent = WTFMove(connectionIdentifiers);
+}
+
+void ServerOpenDBRequest::connectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier)
+{
+ m_connectionsPendingVersionChangeEvent.remove(connectionIdentifier);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h
new file mode 100644
index 000000000..349c8f43b
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToClient.h"
+#include "IDBRequestData.h"
+#include <wtf/HashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBDatabaseInfo;
+
+namespace IDBServer {
+
+class ServerOpenDBRequest : public RefCounted<ServerOpenDBRequest> {
+public:
+ static Ref<ServerOpenDBRequest> create(IDBConnectionToClient&, const IDBRequestData&);
+
+ IDBConnectionToClient& connection() { return m_connection; }
+ const IDBRequestData& requestData() const { return m_requestData; }
+
+ bool isOpenRequest() const;
+ bool isDeleteRequest() const;
+
+ void maybeNotifyRequestBlocked(uint64_t currentVersion);
+ void notifyDidDeleteDatabase(const IDBDatabaseInfo&);
+
+ uint64_t versionChangeID() const;
+
+ void notifiedConnectionsOfVersionChange(HashSet<uint64_t>&& connectionIdentifiers);
+ void connectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier);
+ bool hasConnectionsPendingVersionChangeEvent() const { return !m_connectionsPendingVersionChangeEvent.isEmpty(); }
+ bool hasNotifiedConnectionsOfVersionChange() const { return m_notifiedConnectionsOfVersionChange; }
+
+
+private:
+ ServerOpenDBRequest(IDBConnectionToClient&, const IDBRequestData&);
+
+ IDBConnectionToClient& m_connection;
+ IDBRequestData m_requestData;
+
+ bool m_notifiedBlocked { false };
+
+ bool m_notifiedConnectionsOfVersionChange { false };
+ HashSet<uint64_t> m_connectionsPendingVersionChangeEvent;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp
new file mode 100644
index 000000000..87863f9a3
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp
@@ -0,0 +1,1908 @@
+/*
+ * Copyright (C) 2015, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "UniqueIDBDatabase.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursorInfo.h"
+#include "IDBGetAllRecordsData.h"
+#include "IDBGetAllResult.h"
+#include "IDBGetRecordData.h"
+#include "IDBIterateCursorData.h"
+#include "IDBKeyRangeData.h"
+#include "IDBResultData.h"
+#include "IDBServer.h"
+#include "IDBTransactionInfo.h"
+#include "IDBValue.h"
+#include "Logging.h"
+#include "ScopeGuard.h"
+#include "SerializedScriptValue.h"
+#include "UniqueIDBDatabaseConnection.h"
+#include <heap/HeapInlines.h>
+#include <heap/StrongInlines.h>
+#include <runtime/AuxiliaryBarrierInlines.h>
+#include <runtime/StructureInlines.h>
+#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
+
+using namespace JSC;
+
+namespace WebCore {
+namespace IDBServer {
+
+UniqueIDBDatabase::UniqueIDBDatabase(IDBServer& server, const IDBDatabaseIdentifier& identifier)
+ : m_server(server)
+ , m_identifier(identifier)
+ , m_operationAndTransactionTimer(*this, &UniqueIDBDatabase::operationAndTransactionTimerFired)
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data());
+}
+
+UniqueIDBDatabase::~UniqueIDBDatabase()
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::~UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data());
+ ASSERT(isMainThread());
+ ASSERT(!hasAnyPendingCallbacks());
+ ASSERT(!hasUnfinishedTransactions());
+ ASSERT(m_pendingTransactions.isEmpty());
+ ASSERT(m_openDatabaseConnections.isEmpty());
+ ASSERT(m_clientClosePendingDatabaseConnections.isEmpty());
+ ASSERT(m_serverClosePendingDatabaseConnections.isEmpty());
+ ASSERT(!m_queuedTaskCount);
+}
+
+const IDBDatabaseInfo& UniqueIDBDatabase::info() const
+{
+ RELEASE_ASSERT(m_databaseInfo);
+ return *m_databaseInfo;
+}
+
+void UniqueIDBDatabase::openDatabaseConnection(IDBConnectionToClient& connection, const IDBRequestData& requestData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::openDatabaseConnection");
+ ASSERT(!m_hardClosedForUserDelete);
+
+ m_pendingOpenDBRequests.add(ServerOpenDBRequest::create(connection, requestData));
+
+ // An open operation is already in progress, so we can't possibly handle this one yet.
+ if (m_isOpeningBackingStore)
+ return;
+
+ handleDatabaseOperations();
+}
+
+bool UniqueIDBDatabase::hasAnyPendingCallbacks() const
+{
+ return !m_errorCallbacks.isEmpty()
+ || !m_keyDataCallbacks.isEmpty()
+ || !m_getResultCallbacks.isEmpty()
+ || !m_getAllResultsCallbacks.isEmpty()
+ || !m_countCallbacks.isEmpty();
+}
+
+bool UniqueIDBDatabase::isVersionChangeInProgress()
+{
+#if !LOG_DISABLED
+ if (m_versionChangeTransaction)
+ ASSERT(m_versionChangeDatabaseConnection);
+#endif
+
+ return m_versionChangeDatabaseConnection;
+}
+
+void UniqueIDBDatabase::performCurrentOpenOperation()
+{
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentOpenOperation (%p)", this);
+
+ ASSERT(m_currentOpenDBRequest);
+ ASSERT(m_currentOpenDBRequest->isOpenRequest());
+
+ if (!m_databaseInfo) {
+ if (!m_isOpeningBackingStore) {
+ m_isOpeningBackingStore = true;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier));
+ }
+
+ return;
+ }
+
+ // If we previously started a version change operation but were blocked by having open connections,
+ // we might now be unblocked.
+ if (m_versionChangeDatabaseConnection) {
+ if (!m_versionChangeTransaction && !hasAnyOpenConnections())
+ startVersionChangeTransaction();
+ return;
+ }
+
+ // 3.3.1 Opening a database
+ // If requested version is undefined, then let requested version be 1 if db was created in the previous step,
+ // or the current version of db otherwise.
+ uint64_t requestedVersion = m_currentOpenDBRequest->requestData().requestedVersion();
+ if (!requestedVersion)
+ requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
+
+ // 3.3.1 Opening a database
+ // If the database version higher than the requested version, abort these steps and return a VersionError.
+ if (requestedVersion < m_databaseInfo->version()) {
+ auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), IDBError(IDBDatabaseException::VersionError));
+ m_currentOpenDBRequest->connection().didOpenDatabase(result);
+ m_currentOpenDBRequest = nullptr;
+
+ return;
+ }
+
+ if (!m_backingStoreOpenError.isNull()) {
+ auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), m_backingStoreOpenError);
+ m_currentOpenDBRequest->connection().didOpenDatabase(result);
+ m_currentOpenDBRequest = nullptr;
+
+ return;
+ }
+
+ Ref<UniqueIDBDatabaseConnection> connection = UniqueIDBDatabaseConnection::create(*this, *m_currentOpenDBRequest);
+
+ if (requestedVersion == m_databaseInfo->version()) {
+ auto* rawConnection = &connection.get();
+ addOpenDatabaseConnection(WTFMove(connection));
+
+ auto result = IDBResultData::openDatabaseSuccess(m_currentOpenDBRequest->requestData().requestIdentifier(), *rawConnection);
+ m_currentOpenDBRequest->connection().didOpenDatabase(result);
+ m_currentOpenDBRequest = nullptr;
+
+ return;
+ }
+
+ ASSERT(!m_versionChangeDatabaseConnection);
+ m_versionChangeDatabaseConnection = WTFMove(connection);
+
+ // 3.3.7 "versionchange" transaction steps
+ // If there's no other open connections to this database, the version change process can begin immediately.
+ if (!hasAnyOpenConnections()) {
+ startVersionChangeTransaction();
+ return;
+ }
+
+ // Otherwise we have to notify all those open connections and wait for them to close.
+ maybeNotifyConnectionsOfVersionChange();
+}
+
+void UniqueIDBDatabase::performCurrentDeleteOperation()
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentDeleteOperation - %s", m_identifier.debugString().utf8().data());
+
+ ASSERT(m_currentOpenDBRequest);
+ ASSERT(m_currentOpenDBRequest->isDeleteRequest());
+
+ if (m_deleteBackingStoreInProgress)
+ return;
+
+ if (hasAnyOpenConnections()) {
+ maybeNotifyConnectionsOfVersionChange();
+ return;
+ }
+
+ if (hasUnfinishedTransactions())
+ return;
+
+ ASSERT(!hasAnyPendingCallbacks());
+ ASSERT(m_pendingTransactions.isEmpty());
+ ASSERT(m_openDatabaseConnections.isEmpty());
+
+ // It's possible to have multiple delete requests queued up in a row.
+ // In that scenario only the first request will actually have to delete the database.
+ // Subsequent requests can immediately notify their completion.
+
+ if (!m_deleteBackingStoreInProgress) {
+ if (!m_databaseInfo && m_mostRecentDeletedDatabaseInfo)
+ didDeleteBackingStore(0);
+ else {
+ m_deleteBackingStoreInProgress = true;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::deleteBackingStore, m_identifier));
+ }
+ }
+}
+
+void UniqueIDBDatabase::deleteBackingStore(const IDBDatabaseIdentifier& identifier)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::deleteBackingStore");
+
+ uint64_t deletedVersion = 0;
+
+ if (m_backingStore) {
+ m_backingStore->deleteBackingStore();
+ m_backingStore = nullptr;
+ m_backingStoreSupportsSimultaneousTransactions = false;
+ m_backingStoreIsEphemeral = false;
+ } else {
+ auto backingStore = m_server.createBackingStore(identifier);
+
+ IDBDatabaseInfo databaseInfo;
+ auto error = backingStore->getOrEstablishDatabaseInfo(databaseInfo);
+ if (!error.isNull())
+ LOG_ERROR("Error getting database info from database %s that we are trying to delete", identifier.debugString().utf8().data());
+
+ deletedVersion = databaseInfo.version();
+ backingStore->deleteBackingStore();
+ }
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didDeleteBackingStore, deletedVersion));
+}
+
+void UniqueIDBDatabase::performUnconditionalDeleteBackingStore()
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performUnconditionalDeleteBackingStore");
+
+ if (!m_backingStore)
+ return;
+
+ m_backingStore->deleteBackingStore();
+ m_backingStore = nullptr;
+ m_backingStoreSupportsSimultaneousTransactions = false;
+ m_backingStoreIsEphemeral = false;
+}
+
+void UniqueIDBDatabase::didDeleteBackingStore(uint64_t deletedVersion)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didDeleteBackingStore");
+
+ ASSERT(!hasAnyPendingCallbacks());
+ ASSERT(!hasUnfinishedTransactions());
+ ASSERT(m_pendingTransactions.isEmpty());
+ ASSERT(m_openDatabaseConnections.isEmpty());
+
+ // It's possible that the openDBRequest was cancelled from client-side after the delete was already dispatched to the backingstore.
+ // So it's okay if we don't have a currentOpenDBRequest, but if we do it has to be a deleteRequest.
+ ASSERT(!m_currentOpenDBRequest || m_currentOpenDBRequest->isDeleteRequest());
+
+ if (m_databaseInfo)
+ m_mostRecentDeletedDatabaseInfo = WTFMove(m_databaseInfo);
+
+ // If this UniqueIDBDatabase was brought into existence for the purpose of deleting the file on disk,
+ // we won't have a m_mostRecentDeletedDatabaseInfo. In that case, we'll manufacture one using the
+ // passed in deletedVersion argument.
+ if (!m_mostRecentDeletedDatabaseInfo)
+ m_mostRecentDeletedDatabaseInfo = std::make_unique<IDBDatabaseInfo>(m_identifier.databaseName(), deletedVersion);
+
+ if (m_currentOpenDBRequest) {
+ m_currentOpenDBRequest->notifyDidDeleteDatabase(*m_mostRecentDeletedDatabaseInfo);
+ m_currentOpenDBRequest = nullptr;
+ }
+
+ m_deleteBackingStoreInProgress = false;
+
+ if (m_clientClosePendingDatabaseConnections.isEmpty() && m_pendingOpenDBRequests.isEmpty()) {
+ m_server.closeUniqueIDBDatabase(*this);
+ return;
+ }
+
+ invokeOperationAndTransactionTimer();
+}
+
+void UniqueIDBDatabase::didPerformUnconditionalDeleteBackingStore()
+{
+ // This function is a placeholder so the database thread can message back to the main thread.
+ ASSERT(m_hardClosedForUserDelete);
+}
+
+void UniqueIDBDatabase::handleDatabaseOperations()
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDatabaseOperations - There are %u pending", m_pendingOpenDBRequests.size());
+ ASSERT(!m_hardClosedForUserDelete);
+
+ if (m_deleteBackingStoreInProgress)
+ return;
+
+ if (m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_currentOpenDBRequest) {
+ // We can't start any new open-database operations right now, but we might be able to start handling a delete operation.
+ if (!m_currentOpenDBRequest && !m_pendingOpenDBRequests.isEmpty() && m_pendingOpenDBRequests.first()->isDeleteRequest())
+ m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst();
+
+ // Some operations (such as the first open operation after a delete) require multiple passes to completely handle
+ if (m_currentOpenDBRequest)
+ handleCurrentOperation();
+
+ return;
+ }
+
+ if (m_pendingOpenDBRequests.isEmpty())
+ return;
+
+ m_currentOpenDBRequest = m_pendingOpenDBRequests.takeFirst();
+ LOG(IndexedDB, "UniqueIDBDatabase::handleDatabaseOperations - Popped an operation, now there are %u pending", m_pendingOpenDBRequests.size());
+
+ handleCurrentOperation();
+}
+
+void UniqueIDBDatabase::handleCurrentOperation()
+{
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::handleCurrentOperation");
+ ASSERT(!m_hardClosedForUserDelete);
+ ASSERT(m_currentOpenDBRequest);
+
+ RefPtr<UniqueIDBDatabase> protectedThis(this);
+
+ if (m_currentOpenDBRequest->isOpenRequest())
+ performCurrentOpenOperation();
+ else if (m_currentOpenDBRequest->isDeleteRequest())
+ performCurrentDeleteOperation();
+ else
+ ASSERT_NOT_REACHED();
+
+ if (!m_currentOpenDBRequest)
+ invokeOperationAndTransactionTimer();
+}
+
+bool UniqueIDBDatabase::hasAnyOpenConnections() const
+{
+ return !m_openDatabaseConnections.isEmpty();
+}
+
+bool UniqueIDBDatabase::allConnectionsAreClosedOrClosing() const
+{
+ for (auto& connection : m_openDatabaseConnections) {
+ if (!connection->connectionIsClosing())
+ return false;
+ }
+
+ return true;
+}
+
+static uint64_t generateUniqueCallbackIdentifier()
+{
+ ASSERT(isMainThread());
+ static uint64_t currentID = 0;
+ return ++currentID;
+}
+
+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback callback)
+{
+ if (m_hardClosedForUserDelete) {
+ callback(IDBError::userDeleteError());
+ return 0;
+ }
+
+ uint64_t identifier = generateUniqueCallbackIdentifier();
+ ASSERT(!m_errorCallbacks.contains(identifier));
+ m_errorCallbacks.add(identifier, callback);
+ return identifier;
+}
+
+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback callback)
+{
+ if (m_hardClosedForUserDelete) {
+ callback(IDBError::userDeleteError(), { });
+ return 0;
+ }
+
+ uint64_t identifier = generateUniqueCallbackIdentifier();
+ ASSERT(!m_keyDataCallbacks.contains(identifier));
+ m_keyDataCallbacks.add(identifier, callback);
+ return identifier;
+}
+
+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(GetResultCallback callback)
+{
+ if (m_hardClosedForUserDelete) {
+ callback(IDBError::userDeleteError(), { });
+ return 0;
+ }
+
+ uint64_t identifier = generateUniqueCallbackIdentifier();
+ ASSERT(!m_getResultCallbacks.contains(identifier));
+ m_getResultCallbacks.add(identifier, callback);
+ return identifier;
+}
+
+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(GetAllResultsCallback callback)
+{
+ if (m_hardClosedForUserDelete) {
+ callback(IDBError::userDeleteError(), { });
+ return 0;
+ }
+
+ uint64_t identifier = generateUniqueCallbackIdentifier();
+ ASSERT(!m_getAllResultsCallbacks.contains(identifier));
+ m_getAllResultsCallbacks.add(identifier, callback);
+ return identifier;
+}
+
+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(CountCallback callback)
+{
+ if (m_hardClosedForUserDelete) {
+ callback(IDBError::userDeleteError(), 0);
+ return 0;
+ }
+
+ uint64_t identifier = generateUniqueCallbackIdentifier();
+ ASSERT(!m_countCallbacks.contains(identifier));
+ m_countCallbacks.add(identifier, callback);
+ return identifier;
+}
+
+void UniqueIDBDatabase::handleDelete(IDBConnectionToClient& connection, const IDBRequestData& requestData)
+{
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::handleDelete");
+ ASSERT(!m_hardClosedForUserDelete);
+
+ m_pendingOpenDBRequests.add(ServerOpenDBRequest::create(connection, requestData));
+ handleDatabaseOperations();
+}
+
+void UniqueIDBDatabase::startVersionChangeTransaction()
+{
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::startVersionChangeTransaction");
+
+ ASSERT(!m_versionChangeTransaction);
+ ASSERT(m_currentOpenDBRequest);
+ ASSERT(m_currentOpenDBRequest->isOpenRequest());
+ ASSERT(m_versionChangeDatabaseConnection);
+
+ auto operation = WTFMove(m_currentOpenDBRequest);
+
+ uint64_t requestedVersion = operation->requestData().requestedVersion();
+ if (!requestedVersion)
+ requestedVersion = m_databaseInfo->version() ? m_databaseInfo->version() : 1;
+
+ addOpenDatabaseConnection(*m_versionChangeDatabaseConnection);
+
+ m_versionChangeTransaction = &m_versionChangeDatabaseConnection->createVersionChangeTransaction(requestedVersion);
+ m_databaseInfo->setVersion(requestedVersion);
+
+ m_inProgressTransactions.set(m_versionChangeTransaction->info().identifier(), m_versionChangeTransaction);
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::beginTransactionInBackingStore, m_versionChangeTransaction->info()));
+
+ auto result = IDBResultData::openDatabaseUpgradeNeeded(operation->requestData().requestIdentifier(), *m_versionChangeTransaction);
+ operation->connection().didOpenDatabase(result);
+}
+
+void UniqueIDBDatabase::beginTransactionInBackingStore(const IDBTransactionInfo& info)
+{
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::beginTransactionInBackingStore");
+ m_backingStore->beginTransaction(info);
+}
+
+void UniqueIDBDatabase::maybeNotifyConnectionsOfVersionChange()
+{
+ ASSERT(m_currentOpenDBRequest);
+
+ if (m_currentOpenDBRequest->hasNotifiedConnectionsOfVersionChange())
+ return;
+
+ uint64_t newVersion = m_currentOpenDBRequest->isOpenRequest() ? m_currentOpenDBRequest->requestData().requestedVersion() : 0;
+ auto requestIdentifier = m_currentOpenDBRequest->requestData().requestIdentifier();
+
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::notifyConnectionsOfVersionChange - %" PRIu64, newVersion);
+
+ // 3.3.7 "versionchange" transaction steps
+ // Fire a versionchange event at each connection in m_openDatabaseConnections that is open.
+ // The event must not be fired on connections which has the closePending flag set.
+ HashSet<uint64_t> connectionIdentifiers;
+ for (auto connection : m_openDatabaseConnections) {
+ if (connection->closePending())
+ continue;
+
+ connection->fireVersionChangeEvent(requestIdentifier, newVersion);
+ connectionIdentifiers.add(connection->identifier());
+ }
+
+ if (!connectionIdentifiers.isEmpty())
+ m_currentOpenDBRequest->notifiedConnectionsOfVersionChange(WTFMove(connectionIdentifiers));
+ else
+ m_currentOpenDBRequest->maybeNotifyRequestBlocked(m_databaseInfo->version());
+}
+
+void UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier)
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent - %" PRIu64, connectionIdentifier);
+
+ ASSERT(m_currentOpenDBRequest);
+
+ m_currentOpenDBRequest->connectionClosedOrFiredVersionChangeEvent(connectionIdentifier);
+
+ if (m_currentOpenDBRequest->hasConnectionsPendingVersionChangeEvent())
+ return;
+
+ if (!hasAnyOpenConnections() || allConnectionsAreClosedOrClosing()) {
+ invokeOperationAndTransactionTimer();
+ return;
+ }
+
+ // Since all open connections have fired their version change events but not all of them have closed,
+ // this request is officially blocked.
+ m_currentOpenDBRequest->maybeNotifyRequestBlocked(m_databaseInfo->version());
+}
+
+void UniqueIDBDatabase::didFireVersionChangeEvent(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier)
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::didFireVersionChangeEvent");
+
+ if (!m_currentOpenDBRequest)
+ return;
+
+ ASSERT_UNUSED(requestIdentifier, m_currentOpenDBRequest->requestData().requestIdentifier() == requestIdentifier);
+
+ notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier());
+}
+
+void UniqueIDBDatabase::openDBRequestCancelled(const IDBResourceIdentifier& requestIdentifier)
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::openDBRequestCancelled - %s", requestIdentifier.loggingString().utf8().data());
+
+ if (m_currentOpenDBRequest && m_currentOpenDBRequest->requestData().requestIdentifier() == requestIdentifier)
+ m_currentOpenDBRequest = nullptr;
+
+ if (m_versionChangeDatabaseConnection && m_versionChangeDatabaseConnection->openRequestIdentifier() == requestIdentifier) {
+ ASSERT(!m_versionChangeTransaction || m_versionChangeTransaction->databaseConnection().openRequestIdentifier() == requestIdentifier);
+ ASSERT(!m_versionChangeTransaction || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
+
+ connectionClosedFromClient(*m_versionChangeDatabaseConnection);
+ }
+
+ for (auto& request : m_pendingOpenDBRequests) {
+ if (request->requestData().requestIdentifier() == requestIdentifier) {
+ m_pendingOpenDBRequests.remove(request);
+ return;
+ }
+ }
+}
+
+void UniqueIDBDatabase::addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&& connection)
+{
+ ASSERT(!m_openDatabaseConnections.contains(&connection.get()));
+ m_openDatabaseConnections.add(adoptRef(connection.leakRef()));
+}
+
+void UniqueIDBDatabase::openBackingStore(const IDBDatabaseIdentifier& identifier)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::openBackingStore (%p)", this);
+
+ ASSERT(!m_backingStore);
+ m_backingStore = m_server.createBackingStore(identifier);
+ m_backingStoreSupportsSimultaneousTransactions = m_backingStore->supportsSimultaneousTransactions();
+ m_backingStoreIsEphemeral = m_backingStore->isEphemeral();
+
+ IDBDatabaseInfo databaseInfo;
+ auto error = m_backingStore->getOrEstablishDatabaseInfo(databaseInfo);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo, error));
+}
+
+void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info, const IDBError& error)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didOpenBackingStore");
+
+ m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
+ m_backingStoreOpenError = error;
+
+ ASSERT(m_isOpeningBackingStore);
+ m_isOpeningBackingStore = false;
+
+ handleDatabaseOperations();
+}
+
+void UniqueIDBDatabase::createObjectStore(UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::createObjectStore");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateObjectStore, callbackID, transaction.info().identifier(), info));
+}
+
+void UniqueIDBDatabase::performCreateObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& info)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateObjectStore");
+
+ ASSERT(m_backingStore);
+ m_backingStore->createObjectStore(transactionIdentifier, info);
+
+ IDBError error;
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateObjectStore, callbackIdentifier, error, info));
+}
+
+void UniqueIDBDatabase::didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError& error, const IDBObjectStoreInfo& info)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateObjectStore");
+
+ if (error.isNull())
+ m_databaseInfo->addExistingObjectStore(info);
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::deleteObjectStore(UniqueIDBDatabaseTransaction& transaction, const String& objectStoreName, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteObjectStore");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ auto* info = m_databaseInfo->infoForExistingObjectStore(objectStoreName);
+ if (!info) {
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete non-existant object store") });
+ return;
+ }
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteObjectStore, callbackID, transaction.info().identifier(), info->identifier()));
+}
+
+void UniqueIDBDatabase::performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteObjectStore");
+
+ ASSERT(m_backingStore);
+ m_backingStore->deleteObjectStore(transactionIdentifier, objectStoreIdentifier);
+
+ IDBError error;
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteObjectStore, callbackIdentifier, error, objectStoreIdentifier));
+}
+
+void UniqueIDBDatabase::didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteObjectStore");
+
+ if (error.isNull())
+ m_databaseInfo->deleteObjectStore(objectStoreIdentifier);
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::renameObjectStore(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::renameObjectStore");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ auto* info = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ if (!info) {
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename non-existant object store") });
+ return;
+ }
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameObjectStore, callbackID, transaction.info().identifier(), objectStoreIdentifier, newName));
+}
+
+void UniqueIDBDatabase::performRenameObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performRenameObjectStore");
+
+ ASSERT(m_backingStore);
+ m_backingStore->renameObjectStore(transactionIdentifier, objectStoreIdentifier, newName);
+
+ IDBError error;
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformRenameObjectStore, callbackIdentifier, error, objectStoreIdentifier, newName));
+}
+
+void UniqueIDBDatabase::didPerformRenameObjectStore(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, const String& newName)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameObjectStore");
+
+ if (error.isNull())
+ m_databaseInfo->renameObjectStore(objectStoreIdentifier, newName);
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::clearObjectStore(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::clearObjectStore");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performClearObjectStore, callbackID, transaction.info().identifier(), objectStoreIdentifier));
+}
+
+void UniqueIDBDatabase::performClearObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performClearObjectStore");
+
+ ASSERT(m_backingStore);
+ m_backingStore->clearObjectStore(transactionIdentifier, objectStoreIdentifier);
+
+ IDBError error;
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformClearObjectStore, callbackIdentifier, error));
+}
+
+void UniqueIDBDatabase::didPerformClearObjectStore(uint64_t callbackIdentifier, const IDBError& error)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformClearObjectStore");
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::createIndex(UniqueIDBDatabaseTransaction& transaction, const IDBIndexInfo& info, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::createIndex");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateIndex, callbackID, transaction.info().identifier(), info));
+}
+
+void UniqueIDBDatabase::performCreateIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo& info)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performCreateIndex");
+
+ ASSERT(m_backingStore);
+ IDBError error = m_backingStore->createIndex(transactionIdentifier, info);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCreateIndex, callbackIdentifier, error, info));
+}
+
+void UniqueIDBDatabase::didPerformCreateIndex(uint64_t callbackIdentifier, const IDBError& error, const IDBIndexInfo& info)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCreateIndex");
+
+ if (error.isNull()) {
+ ASSERT(m_databaseInfo);
+ auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(info.objectStoreIdentifier());
+ ASSERT(objectStoreInfo);
+ objectStoreInfo->addExistingIndex(info);
+ }
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::deleteIndex(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& indexName, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteIndex");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ if (!objectStoreInfo) {
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete index from non-existant object store") });
+ return;
+ }
+
+ auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexName);
+ if (!indexInfo) {
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to delete non-existant index") });
+ return;
+ }
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexInfo->identifier()));
+}
+
+void UniqueIDBDatabase::performDeleteIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const uint64_t indexIdentifier)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteIndex");
+
+ ASSERT(m_backingStore);
+ m_backingStore->deleteIndex(transactionIdentifier, objectStoreIdentifier, indexIdentifier);
+
+ IDBError error;
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteIndex, callbackIdentifier, error, objectStoreIdentifier, indexIdentifier));
+}
+
+void UniqueIDBDatabase::didPerformDeleteIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, uint64_t indexIdentifier)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteIndex");
+
+ if (error.isNull()) {
+ auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ if (objectStoreInfo)
+ objectStoreInfo->deleteIndex(indexIdentifier);
+ }
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::renameIndex(UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::renameIndex");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ if (!objectStoreInfo) {
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename index in non-existant object store") });
+ return;
+ }
+
+ auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier);
+ if (!indexInfo) {
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to rename non-existant index") });
+ return;
+ }
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performRenameIndex, callbackID, transaction.info().identifier(), objectStoreIdentifier, indexIdentifier, newName));
+}
+
+void UniqueIDBDatabase::performRenameIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performRenameIndex");
+
+ ASSERT(m_backingStore);
+ m_backingStore->renameIndex(transactionIdentifier, objectStoreIdentifier, indexIdentifier, newName);
+
+ IDBError error;
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformRenameIndex, callbackIdentifier, error, objectStoreIdentifier, indexIdentifier, newName));
+}
+
+void UniqueIDBDatabase::didPerformRenameIndex(uint64_t callbackIdentifier, const IDBError& error, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformRenameIndex");
+
+ if (error.isNull()) {
+ auto* objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
+ ASSERT(objectStoreInfo);
+ if (objectStoreInfo) {
+ auto* indexInfo = objectStoreInfo->infoForExistingIndex(indexIdentifier);
+ ASSERT(indexInfo);
+ indexInfo->rename(newName);
+ }
+ }
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode, KeyDataCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::putOrAdd");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPutOrAdd, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyData, value, overwriteMode));
+}
+
+VM& UniqueIDBDatabase::databaseThreadVM()
+{
+ ASSERT(!isMainThread());
+ static VM* vm = &VM::create().leakRef();
+ return *vm;
+}
+
+ExecState& UniqueIDBDatabase::databaseThreadExecState()
+{
+ ASSERT(!isMainThread());
+
+ static NeverDestroyed<Strong<JSGlobalObject>> globalObject(databaseThreadVM(), JSGlobalObject::create(databaseThreadVM(), JSGlobalObject::createStructure(databaseThreadVM(), jsNull())));
+
+ RELEASE_ASSERT(globalObject.get()->globalExec());
+ return *globalObject.get()->globalExec();
+}
+
+void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, const IDBValue& originalRecordValue, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performPutOrAdd");
+
+ ASSERT(m_backingStore);
+ ASSERT(objectStoreIdentifier);
+
+ IDBKeyData usedKey;
+ IDBError error;
+
+ auto* objectStoreInfo = m_backingStore->infoForObjectStore(objectStoreIdentifier);
+ if (!objectStoreInfo) {
+ error = IDBError(IDBDatabaseException::InvalidStateError, ASCIILiteral("Object store cannot be found in the backing store"));
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
+ return;
+ }
+
+ bool usedKeyIsGenerated = false;
+ ScopeGuard generatedKeyResetter;
+ if (objectStoreInfo->autoIncrement() && !keyData.isValid()) {
+ uint64_t keyNumber;
+ error = m_backingStore->generateKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber);
+ if (!error.isNull()) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
+ return;
+ }
+
+ usedKey.setNumberValue(keyNumber);
+ usedKeyIsGenerated = true;
+ generatedKeyResetter.enable([this, transactionIdentifier, objectStoreIdentifier, keyNumber]() {
+ m_backingStore->revertGeneratedKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber);
+ });
+ } else
+ usedKey = keyData;
+
+ if (overwriteMode == IndexedDB::ObjectStoreOverwriteMode::NoOverwrite) {
+ bool keyExists;
+ error = m_backingStore->keyExistsInObjectStore(transactionIdentifier, objectStoreIdentifier, usedKey, keyExists);
+ if (error.isNull() && keyExists)
+ error = IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Key already exists in the object store"));
+
+ if (!error.isNull()) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
+ return;
+ }
+ }
+
+ // 3.4.1.2 Object Store Storage Operation
+ // If ObjectStore has a key path and the key is autogenerated, then inject the key into the value
+ // using steps to assign a key to a value using a key path.
+ ThreadSafeDataBuffer injectedRecordValue;
+ if (usedKeyIsGenerated && objectStoreInfo->keyPath()) {
+ VM& vm = databaseThreadVM();
+ JSLockHolder locker(vm);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto value = deserializeIDBValueToJSValue(databaseThreadExecState(), originalRecordValue.data());
+ if (value.isUndefined()) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Unable to deserialize record value for record key injection")), usedKey));
+ return;
+ }
+
+ if (!injectIDBKeyIntoScriptValue(databaseThreadExecState(), usedKey, value, objectStoreInfo->keyPath().value())) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Unable to inject record key into record value")), usedKey));
+ return;
+ }
+
+ auto serializedValue = SerializedScriptValue::create(databaseThreadExecState(), value);
+ if (UNLIKELY(scope.exception())) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(IDBDatabaseException::ConstraintError, ASCIILiteral("Unable to serialize record value after injecting record key")), usedKey));
+ return;
+ }
+
+ injectedRecordValue = ThreadSafeDataBuffer::copyVector(serializedValue->data());
+ }
+
+ // 3.4.1 Object Store Storage Operation
+ // ...If a record already exists in store ...
+ // then remove the record from store using the steps for deleting records from an object store...
+ // This is important because formally deleting it from from the object store also removes it from the appropriate indexes.
+ error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, usedKey);
+ if (!error.isNull()) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
+ return;
+ }
+
+ if (injectedRecordValue.data())
+ error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, { injectedRecordValue, originalRecordValue.blobURLs(), originalRecordValue.blobFilePaths() });
+ else
+ error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, originalRecordValue);
+
+ if (!error.isNull()) {
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
+ return;
+ }
+
+ if (overwriteMode != IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor && objectStoreInfo->autoIncrement() && keyData.type() == IndexedDB::KeyType::Number)
+ error = m_backingStore->maybeUpdateKeyGeneratorNumber(transactionIdentifier, objectStoreIdentifier, keyData.number());
+
+ generatedKeyResetter.disable();
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
+}
+
+void UniqueIDBDatabase::didPerformPutOrAdd(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformPutOrAdd");
+
+ performKeyDataCallback(callbackIdentifier, error, resultKey);
+}
+
+void UniqueIDBDatabase::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData, GetResultCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::getRecord");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ if (uint64_t indexIdentifier = requestData.indexIdentifier())
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetIndexRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), indexIdentifier, requestData.indexRecordType(), getRecordData.keyRangeData));
+ else
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), getRecordData.keyRangeData, getRecordData.type));
+}
+
+void UniqueIDBDatabase::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData, GetAllResultsCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::getAllRecords");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetAllRecords, callbackID, requestData.transactionIdentifier(), getAllRecordsData));
+}
+
+void UniqueIDBDatabase::performGetRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& keyRangeData, IDBGetRecordDataType type)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetRecord");
+
+ ASSERT(m_backingStore);
+
+ IDBGetResult result;
+ IDBError error = m_backingStore->getRecord(transactionIdentifier, objectStoreIdentifier, keyRangeData, type, result);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetRecord, callbackIdentifier, error, result));
+}
+
+void UniqueIDBDatabase::performGetIndexRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType recordType, const IDBKeyRangeData& range)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetIndexRecord");
+
+ ASSERT(m_backingStore);
+
+ IDBGetResult result;
+ IDBError error = m_backingStore->getIndexRecord(transactionIdentifier, objectStoreIdentifier, indexIdentifier, recordType, range, result);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetRecord, callbackIdentifier, error, result));
+}
+
+void UniqueIDBDatabase::didPerformGetRecord(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetRecord");
+
+ performGetResultCallback(callbackIdentifier, error, result);
+}
+
+void UniqueIDBDatabase::performGetAllRecords(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData& getAllRecordsData)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetAllRecords");
+
+ ASSERT(m_backingStore);
+
+ IDBGetAllResult result;
+ IDBError error = m_backingStore->getAllRecords(transactionIdentifier, getAllRecordsData, result);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetAllRecords, callbackIdentifier, error, WTFMove(result)));
+}
+
+void UniqueIDBDatabase::didPerformGetAllRecords(uint64_t callbackIdentifier, const IDBError& error, const IDBGetAllResult& result)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetAllRecords");
+
+ performGetAllResultsCallback(callbackIdentifier, error, result);
+}
+
+void UniqueIDBDatabase::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& range, CountCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::getCount");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performGetCount, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), requestData.indexIdentifier(), range));
+}
+
+void UniqueIDBDatabase::performGetCount(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& keyRangeData)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performGetCount");
+
+ ASSERT(m_backingStore);
+ ASSERT(objectStoreIdentifier);
+
+ uint64_t count;
+ IDBError error = m_backingStore->getCount(transactionIdentifier, objectStoreIdentifier, indexIdentifier, keyRangeData, count);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformGetCount, callbackIdentifier, error, count));
+}
+
+void UniqueIDBDatabase::didPerformGetCount(uint64_t callbackIdentifier, const IDBError& error, uint64_t count)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformGetCount");
+
+ performCountCallback(callbackIdentifier, error, count);
+}
+
+void UniqueIDBDatabase::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::deleteRecord");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performDeleteRecord, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyRangeData));
+}
+
+void UniqueIDBDatabase::performDeleteRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performDeleteRecord");
+
+ IDBError error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, range);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformDeleteRecord, callbackIdentifier, error));
+}
+
+void UniqueIDBDatabase::didPerformDeleteRecord(uint64_t callbackIdentifier, const IDBError& error)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformDeleteRecord");
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+void UniqueIDBDatabase::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info, GetResultCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::openCursor");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performOpenCursor, callbackID, requestData.transactionIdentifier(), info));
+}
+
+void UniqueIDBDatabase::performOpenCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo& info)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performOpenCursor");
+
+ IDBGetResult result;
+ IDBError error = m_backingStore->openCursor(transactionIdentifier, info, result);
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformOpenCursor, callbackIdentifier, error, result));
+}
+
+void UniqueIDBDatabase::didPerformOpenCursor(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformOpenCursor");
+
+ performGetResultCallback(callbackIdentifier, error, result);
+}
+
+void UniqueIDBDatabase::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data, GetResultCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::iterateCursor");
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performIterateCursor, callbackID, requestData.transactionIdentifier(), requestData.cursorIdentifier(), data));
+}
+
+void UniqueIDBDatabase::performIterateCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData& data)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performIterateCursor");
+
+ IDBGetResult result;
+ IDBError error = m_backingStore->iterateCursor(transactionIdentifier, cursorIdentifier, data, result);
+
+ if (error.isNull()) {
+ auto addResult = m_prefetchProtectors.add(cursorIdentifier, nullptr);
+ if (addResult.isNewEntry) {
+ addResult.iterator->value = this;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier));
+ }
+ }
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformIterateCursor, callbackIdentifier, error, result));
+}
+
+void UniqueIDBDatabase::performPrefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier)
+{
+ ASSERT(!isMainThread());
+ ASSERT(m_prefetchProtectors.contains(cursorIdentifier));
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performPrefetchCursor");
+
+ if (m_backingStore->prefetchCursor(transactionIdentifier, cursorIdentifier))
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPrefetchCursor, transactionIdentifier, cursorIdentifier));
+ else
+ postDatabaseTaskReply(WTF::Function<void ()>([prefetchProtector = m_prefetchProtectors.take(cursorIdentifier)]() { }));
+}
+
+void UniqueIDBDatabase::didPerformIterateCursor(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& result)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformIterateCursor");
+
+ performGetResultCallback(callbackIdentifier, error, result);
+}
+
+bool UniqueIDBDatabase::prepareToFinishTransaction(UniqueIDBDatabaseTransaction& transaction)
+{
+ auto takenTransaction = m_inProgressTransactions.take(transaction.info().identifier());
+ if (!takenTransaction)
+ return false;
+
+ ASSERT(!m_finishingTransactions.contains(transaction.info().identifier()));
+ m_finishingTransactions.set(transaction.info().identifier(), WTFMove(takenTransaction));
+
+ return true;
+}
+
+void UniqueIDBDatabase::commitTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::commitTransaction - %s", transaction.info().identifier().loggingString().utf8().data());
+
+ ASSERT(&transaction.databaseConnection().database() == this);
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ if (!prepareToFinishTransaction(transaction)) {
+ if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) {
+ // This database connection is closing or has already closed, so there is no point in messaging back to it about the commit failing.
+ forgetErrorCallback(callbackID);
+ return;
+ }
+
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to commit transaction that is already finishing") });
+ return;
+ }
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCommitTransaction, callbackID, transaction.info().identifier()));
+}
+
+void UniqueIDBDatabase::performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performCommitTransaction - %s", transactionIdentifier.loggingString().utf8().data());
+
+ IDBError error = m_backingStore->commitTransaction(transactionIdentifier);
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformCommitTransaction, callbackIdentifier, error, transactionIdentifier));
+}
+
+void UniqueIDBDatabase::didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction - %s", transactionIdentifier.loggingString().utf8().data());
+
+ performErrorCallback(callbackIdentifier, error);
+
+ transactionCompleted(m_finishingTransactions.take(transactionIdentifier));
+}
+
+void UniqueIDBDatabase::abortTransaction(UniqueIDBDatabaseTransaction& transaction, ErrorCallback callback)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::abortTransaction - %s", transaction.info().identifier().loggingString().utf8().data());
+
+ ASSERT(&transaction.databaseConnection().database() == this);
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+
+ if (!prepareToFinishTransaction(transaction)) {
+ if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) {
+ // This database connection is closing or has already closed, so there is no point in messaging back to it about the abort failing.
+ forgetErrorCallback(callbackID);
+ return;
+ }
+
+ performErrorCallback(callbackID, { IDBDatabaseException::UnknownError, ASCIILiteral("Attempt to abort transaction that is already finishing") });
+ return;
+ }
+
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performAbortTransaction, callbackID, transaction.info().identifier()));
+}
+
+void UniqueIDBDatabase::didFinishHandlingVersionChange(UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& transactionIdentifier)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didFinishHandlingVersionChange");
+
+ ASSERT_UNUSED(transactionIdentifier, !m_versionChangeTransaction || m_versionChangeTransaction->info().identifier() == transactionIdentifier);
+ ASSERT_UNUSED(connection, !m_versionChangeDatabaseConnection || m_versionChangeDatabaseConnection.get() == &connection);
+
+ m_versionChangeTransaction = nullptr;
+ m_versionChangeDatabaseConnection = nullptr;
+
+ if (m_hardClosedForUserDelete) {
+ maybeFinishHardClose();
+ return;
+ }
+
+ invokeOperationAndTransactionTimer();
+}
+
+void UniqueIDBDatabase::performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ ASSERT(!isMainThread());
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performAbortTransaction - %s", transactionIdentifier.loggingString().utf8().data());
+
+ IDBError error = m_backingStore->abortTransaction(transactionIdentifier);
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformAbortTransaction, callbackIdentifier, error, transactionIdentifier));
+}
+
+void UniqueIDBDatabase::didPerformAbortTransaction(uint64_t callbackIdentifier, const IDBError& error, const IDBResourceIdentifier& transactionIdentifier)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformAbortTransaction - %s", transactionIdentifier.loggingString().utf8().data());
+
+ auto transaction = m_finishingTransactions.take(transactionIdentifier);
+ ASSERT(transaction);
+
+ if (m_versionChangeTransaction && m_versionChangeTransaction->info().identifier() == transactionIdentifier) {
+ ASSERT(m_versionChangeTransaction == transaction);
+ ASSERT(!m_versionChangeDatabaseConnection || &m_versionChangeTransaction->databaseConnection() == m_versionChangeDatabaseConnection);
+ ASSERT(m_versionChangeTransaction->originalDatabaseInfo());
+ m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*m_versionChangeTransaction->originalDatabaseInfo());
+ }
+
+ performErrorCallback(callbackIdentifier, error);
+
+ transactionCompleted(WTFMove(transaction));
+}
+
+void UniqueIDBDatabase::transactionDestroyed(UniqueIDBDatabaseTransaction& transaction)
+{
+ if (m_versionChangeTransaction == &transaction)
+ m_versionChangeTransaction = nullptr;
+}
+
+void UniqueIDBDatabase::connectionClosedFromClient(UniqueIDBDatabaseConnection& connection)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::connectionClosedFromClient - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier());
+
+ Ref<UniqueIDBDatabaseConnection> protectedConnection(connection);
+ m_openDatabaseConnections.remove(&connection);
+
+ if (m_versionChangeDatabaseConnection == &connection) {
+ if (m_versionChangeTransaction) {
+ m_clientClosePendingDatabaseConnections.add(WTFMove(m_versionChangeDatabaseConnection));
+
+ auto transactionIdentifier = m_versionChangeTransaction->info().identifier();
+ if (m_inProgressTransactions.contains(transactionIdentifier)) {
+ ASSERT(!m_finishingTransactions.contains(transactionIdentifier));
+ connection.abortTransactionWithoutCallback(*m_versionChangeTransaction);
+ }
+
+ return;
+ }
+
+ m_versionChangeDatabaseConnection = nullptr;
+ }
+
+ Deque<RefPtr<UniqueIDBDatabaseTransaction>> pendingTransactions;
+ while (!m_pendingTransactions.isEmpty()) {
+ auto transaction = m_pendingTransactions.takeFirst();
+ if (&transaction->databaseConnection() != &connection)
+ pendingTransactions.append(WTFMove(transaction));
+ }
+
+ if (!pendingTransactions.isEmpty())
+ m_pendingTransactions.swap(pendingTransactions);
+
+ Deque<RefPtr<UniqueIDBDatabaseTransaction>> transactionsToAbort;
+ for (auto& transaction : m_inProgressTransactions.values()) {
+ if (&transaction->databaseConnection() == &connection)
+ transactionsToAbort.append(transaction);
+ }
+
+ for (auto& transaction : transactionsToAbort)
+ transaction->abortWithoutCallback();
+
+ if (m_currentOpenDBRequest)
+ notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(connection.identifier());
+
+ if (connection.hasNonFinishedTransactions()) {
+ m_clientClosePendingDatabaseConnections.add(WTFMove(protectedConnection));
+ return;
+ }
+
+ if (m_hardClosedForUserDelete) {
+ maybeFinishHardClose();
+ return;
+ }
+
+ // Now that a database connection has closed, previously blocked operations might be runnable.
+ invokeOperationAndTransactionTimer();
+}
+
+void UniqueIDBDatabase::connectionClosedFromServer(UniqueIDBDatabaseConnection& connection)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "UniqueIDBDatabase::connectionClosedFromServer - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier());
+
+ if (m_clientClosePendingDatabaseConnections.contains(&connection)) {
+ ASSERT(!m_openDatabaseConnections.contains(&connection));
+ ASSERT(!m_serverClosePendingDatabaseConnections.contains(&connection));
+ return;
+ }
+
+ Ref<UniqueIDBDatabaseConnection> protectedConnection(connection);
+ m_openDatabaseConnections.remove(&connection);
+
+ connection.connectionToClient().didCloseFromServer(connection, IDBError::userDeleteError());
+
+ m_serverClosePendingDatabaseConnections.add(WTFMove(protectedConnection));
+}
+
+void UniqueIDBDatabase::confirmDidCloseFromServer(UniqueIDBDatabaseConnection& connection)
+{
+ ASSERT(isMainThread());
+ LOG(IndexedDB, "UniqueIDBDatabase::confirmDidCloseFromServer - %s (%" PRIu64 ")", connection.openRequestIdentifier().loggingString().utf8().data(), connection.identifier());
+
+ ASSERT(m_serverClosePendingDatabaseConnections.contains(&connection));
+ m_serverClosePendingDatabaseConnections.remove(&connection);
+}
+
+void UniqueIDBDatabase::enqueueTransaction(Ref<UniqueIDBDatabaseTransaction>&& transaction)
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::enqueueTransaction - %s", transaction->info().loggingString().utf8().data());
+ ASSERT(!m_hardClosedForUserDelete);
+
+ ASSERT(transaction->info().mode() != IDBTransactionMode::Versionchange);
+
+ m_pendingTransactions.append(WTFMove(transaction));
+
+ invokeOperationAndTransactionTimer();
+}
+
+bool UniqueIDBDatabase::isCurrentlyInUse() const
+{
+ return !m_openDatabaseConnections.isEmpty() || !m_clientClosePendingDatabaseConnections.isEmpty() || !m_pendingOpenDBRequests.isEmpty() || m_currentOpenDBRequest || m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_isOpeningBackingStore || m_deleteBackingStoreInProgress;
+}
+
+bool UniqueIDBDatabase::hasUnfinishedTransactions() const
+{
+ return !m_inProgressTransactions.isEmpty() || !m_finishingTransactions.isEmpty();
+}
+
+void UniqueIDBDatabase::invokeOperationAndTransactionTimer()
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::invokeOperationAndTransactionTimer()");
+ ASSERT(!m_hardClosedForUserDelete);
+
+ if (!m_operationAndTransactionTimer.isActive())
+ m_operationAndTransactionTimer.startOneShot(0);
+}
+
+void UniqueIDBDatabase::operationAndTransactionTimerFired()
+{
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::operationAndTransactionTimerFired");
+ ASSERT(!m_hardClosedForUserDelete);
+
+ RefPtr<UniqueIDBDatabase> protectedThis(this);
+
+ // This UniqueIDBDatabase might be no longer in use by any web page.
+ // Assuming it is not ephemeral, the server should now close it to free up resources.
+ if (!m_backingStoreIsEphemeral && !isCurrentlyInUse()) {
+ ASSERT(m_pendingTransactions.isEmpty());
+ ASSERT(!hasUnfinishedTransactions());
+ m_server.closeUniqueIDBDatabase(*this);
+ return;
+ }
+
+ // The current operation might require multiple attempts to handle, so try to
+ // make further progress on it now.
+ if (m_currentOpenDBRequest)
+ handleCurrentOperation();
+
+ if (!m_currentOpenDBRequest)
+ handleDatabaseOperations();
+
+ bool hadDeferredTransactions = false;
+ auto transaction = takeNextRunnableTransaction(hadDeferredTransactions);
+
+ if (transaction) {
+ m_inProgressTransactions.set(transaction->info().identifier(), transaction);
+ for (auto objectStore : transaction->objectStoreIdentifiers()) {
+ m_objectStoreTransactionCounts.add(objectStore);
+ if (!transaction->isReadOnly()) {
+ m_objectStoreWriteTransactions.add(objectStore);
+ ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1);
+ }
+ }
+
+ activateTransactionInBackingStore(*transaction);
+
+ // If no transactions were deferred, it's possible we can start another transaction right now.
+ if (!hadDeferredTransactions)
+ invokeOperationAndTransactionTimer();
+ }
+}
+
+void UniqueIDBDatabase::activateTransactionInBackingStore(UniqueIDBDatabaseTransaction& transaction)
+{
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::activateTransactionInBackingStore");
+
+ RefPtr<UniqueIDBDatabase> protectedThis(this);
+ RefPtr<UniqueIDBDatabaseTransaction> refTransaction(&transaction);
+
+ auto callback = [this, protectedThis, refTransaction](const IDBError& error) {
+ refTransaction->didActivateInBackingStore(error);
+ };
+
+ uint64_t callbackID = storeCallbackOrFireError(callback);
+ if (!callbackID)
+ return;
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performActivateTransactionInBackingStore, callbackID, transaction.info()));
+}
+
+void UniqueIDBDatabase::performActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBTransactionInfo& info)
+{
+ LOG(IndexedDB, "(db) UniqueIDBDatabase::performActivateTransactionInBackingStore");
+
+ IDBError error = m_backingStore->beginTransaction(info);
+ postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformActivateTransactionInBackingStore, callbackIdentifier, error));
+}
+
+void UniqueIDBDatabase::didPerformActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBError& error)
+{
+ LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformActivateTransactionInBackingStore");
+
+ invokeOperationAndTransactionTimer();
+
+ performErrorCallback(callbackIdentifier, error);
+}
+
+template<typename T> bool scopesOverlap(const T& aScopes, const Vector<uint64_t>& bScopes)
+{
+ for (auto scope : bScopes) {
+ if (aScopes.contains(scope))
+ return true;
+ }
+
+ return false;
+}
+
+RefPtr<UniqueIDBDatabaseTransaction> UniqueIDBDatabase::takeNextRunnableTransaction(bool& hadDeferredTransactions)
+{
+ hadDeferredTransactions = false;
+
+ if (m_pendingTransactions.isEmpty())
+ return nullptr;
+
+ if (!m_backingStoreSupportsSimultaneousTransactions && hasUnfinishedTransactions()) {
+ LOG(IndexedDB, "UniqueIDBDatabase::takeNextRunnableTransaction - Backing store only supports 1 transaction, and we already have 1");
+ return nullptr;
+ }
+
+ Deque<RefPtr<UniqueIDBDatabaseTransaction>> deferredTransactions;
+ RefPtr<UniqueIDBDatabaseTransaction> currentTransaction;
+
+ HashSet<uint64_t> deferredReadWriteScopes;
+
+ while (!m_pendingTransactions.isEmpty()) {
+ currentTransaction = m_pendingTransactions.takeFirst();
+
+ switch (currentTransaction->info().mode()) {
+ case IDBTransactionMode::Readonly: {
+ bool hasOverlappingScopes = scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers());
+ hasOverlappingScopes |= scopesOverlap(m_objectStoreWriteTransactions, currentTransaction->objectStoreIdentifiers());
+
+ if (hasOverlappingScopes)
+ deferredTransactions.append(WTFMove(currentTransaction));
+
+ break;
+ }
+ case IDBTransactionMode::Readwrite: {
+ bool hasOverlappingScopes = scopesOverlap(m_objectStoreTransactionCounts, currentTransaction->objectStoreIdentifiers());
+ hasOverlappingScopes |= scopesOverlap(deferredReadWriteScopes, currentTransaction->objectStoreIdentifiers());
+
+ if (hasOverlappingScopes) {
+ for (auto objectStore : currentTransaction->objectStoreIdentifiers())
+ deferredReadWriteScopes.add(objectStore);
+ deferredTransactions.append(WTFMove(currentTransaction));
+ }
+
+ break;
+ }
+ case IDBTransactionMode::Versionchange:
+ // Version change transactions should never be scheduled in the traditional manner.
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ // If we didn't defer the currentTransaction above, it can be run now.
+ if (currentTransaction)
+ break;
+ }
+
+ hadDeferredTransactions = !deferredTransactions.isEmpty();
+ if (!hadDeferredTransactions)
+ return currentTransaction;
+
+ // Prepend the deferred transactions back on the beginning of the deque for future scheduling passes.
+ while (!deferredTransactions.isEmpty())
+ m_pendingTransactions.prepend(deferredTransactions.takeLast());
+
+ return currentTransaction;
+}
+
+void UniqueIDBDatabase::transactionCompleted(RefPtr<UniqueIDBDatabaseTransaction>&& transaction)
+{
+ ASSERT(transaction);
+ ASSERT(!m_inProgressTransactions.contains(transaction->info().identifier()));
+ ASSERT(!m_finishingTransactions.contains(transaction->info().identifier()));
+
+ for (auto objectStore : transaction->objectStoreIdentifiers()) {
+ if (!transaction->isReadOnly()) {
+ m_objectStoreWriteTransactions.remove(objectStore);
+ ASSERT(m_objectStoreTransactionCounts.count(objectStore) == 1);
+ }
+ m_objectStoreTransactionCounts.remove(objectStore);
+ }
+
+ if (!transaction->databaseConnection().hasNonFinishedTransactions())
+ m_clientClosePendingDatabaseConnections.remove(&transaction->databaseConnection());
+
+ if (m_versionChangeTransaction == transaction)
+ m_versionChangeTransaction = nullptr;
+
+ // It's possible that this database had its backing store deleted but there were a few outstanding asynchronous operations.
+ // If this transaction completing was the last of those operations, we can finally delete this UniqueIDBDatabase.
+ if (m_clientClosePendingDatabaseConnections.isEmpty() && m_pendingOpenDBRequests.isEmpty() && !m_databaseInfo) {
+ m_server.closeUniqueIDBDatabase(*this);
+ return;
+ }
+
+ // Previously blocked operations might be runnable.
+ if (!m_hardClosedForUserDelete)
+ invokeOperationAndTransactionTimer();
+ else
+ maybeFinishHardClose();
+}
+
+void UniqueIDBDatabase::postDatabaseTask(CrossThreadTask&& task)
+{
+ m_databaseQueue.append([protectedThis = makeRef(*this), task = WTFMove(task)]() mutable {
+ task.performTask();
+ });
+ ++m_queuedTaskCount;
+
+ m_server.postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTask));
+}
+
+void UniqueIDBDatabase::postDatabaseTaskReply(CrossThreadTask&& task)
+{
+ ASSERT(!isMainThread());
+
+ m_databaseReplyQueue.append([protectedThis = makeRef(*this), task = WTFMove(task)]() mutable {
+ task.performTask();
+ });
+ ++m_queuedTaskCount;
+
+ m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTaskReply));
+}
+
+void UniqueIDBDatabase::executeNextDatabaseTask()
+{
+ ASSERT(!isMainThread());
+ ASSERT(m_queuedTaskCount);
+
+ auto task = m_databaseQueue.tryGetMessage();
+ ASSERT(task);
+
+ (*task)();
+ --m_queuedTaskCount;
+
+ // Release the task on the main thread in case it holds the last reference to this,
+ // as UniqueIDBDatabase objects must be deleted on the main thread.
+ callOnMainThread([task = WTFMove(task)] {
+ });
+}
+
+void UniqueIDBDatabase::executeNextDatabaseTaskReply()
+{
+ ASSERT(isMainThread());
+ ASSERT(m_queuedTaskCount);
+
+ auto task = m_databaseReplyQueue.tryGetMessage();
+ ASSERT(task);
+
+ (*task)();
+ --m_queuedTaskCount;
+
+ // If this database was force closed (e.g. for a user delete) and there are no more
+ // cleanup tasks left, delete this.
+ maybeFinishHardClose();
+}
+
+void UniqueIDBDatabase::maybeFinishHardClose()
+{
+ if (m_hardCloseProtector && isDoneWithHardClose()) {
+ callOnMainThread([this] {
+ ASSERT(isDoneWithHardClose());
+ m_hardCloseProtector = nullptr;
+ });
+ }
+}
+
+bool UniqueIDBDatabase::isDoneWithHardClose()
+{
+ return !m_queuedTaskCount && m_clientClosePendingDatabaseConnections.isEmpty() && m_serverClosePendingDatabaseConnections.isEmpty();
+}
+
+static void errorOpenDBRequestForUserDelete(ServerOpenDBRequest& request)
+{
+ auto result = IDBResultData::error(request.requestData().requestIdentifier(), IDBError::userDeleteError());
+ if (request.isOpenRequest())
+ request.connection().didOpenDatabase(result);
+ else
+ request.connection().didDeleteDatabase(result);
+}
+
+void UniqueIDBDatabase::immediateCloseForUserDelete()
+{
+ LOG(IndexedDB, "UniqueIDBDatabase::immediateCloseForUserDelete - Cancelling (%i, %i, %i, %i) callbacks", m_errorCallbacks.size(), m_keyDataCallbacks.size(), m_getResultCallbacks.size(), m_countCallbacks.size());
+
+ // Error out all transactions
+ Vector<IDBResourceIdentifier> inProgressIdentifiers;
+ copyKeysToVector(m_inProgressTransactions, inProgressIdentifiers);
+ for (auto& identifier : inProgressIdentifiers)
+ m_inProgressTransactions.get(identifier)->abortWithoutCallback();
+
+ ASSERT(m_inProgressTransactions.isEmpty());
+
+ m_pendingTransactions.clear();
+ m_objectStoreTransactionCounts.clear();
+ m_objectStoreWriteTransactions.clear();
+
+ // Error out all pending callbacks
+ Vector<uint64_t> callbackIdentifiers;
+ IDBError error = IDBError::userDeleteError();
+ IDBKeyData keyData;
+ IDBGetResult getResult;
+
+ copyKeysToVector(m_errorCallbacks, callbackIdentifiers);
+ for (auto identifier : callbackIdentifiers)
+ performErrorCallback(identifier, error);
+
+ callbackIdentifiers.clear();
+ copyKeysToVector(m_keyDataCallbacks, callbackIdentifiers);
+ for (auto identifier : callbackIdentifiers)
+ performKeyDataCallback(identifier, error, keyData);
+
+ callbackIdentifiers.clear();
+ copyKeysToVector(m_getResultCallbacks, callbackIdentifiers);
+ for (auto identifier : callbackIdentifiers)
+ performGetResultCallback(identifier, error, getResult);
+
+ callbackIdentifiers.clear();
+ copyKeysToVector(m_countCallbacks, callbackIdentifiers);
+ for (auto identifier : callbackIdentifiers)
+ performCountCallback(identifier, error, 0);
+
+ // Error out all IDBOpenDBRequests
+ if (m_currentOpenDBRequest) {
+ errorOpenDBRequestForUserDelete(*m_currentOpenDBRequest);
+ m_currentOpenDBRequest = nullptr;
+ }
+
+ for (auto& request : m_pendingOpenDBRequests)
+ errorOpenDBRequestForUserDelete(*request);
+
+ m_pendingOpenDBRequests.clear();
+
+ // Close all open connections
+ ListHashSet<RefPtr<UniqueIDBDatabaseConnection>> openDatabaseConnections = m_openDatabaseConnections;
+ for (auto& connection : openDatabaseConnections)
+ connectionClosedFromServer(*connection);
+
+ // Cancel the operation timer
+ m_operationAndTransactionTimer.stop();
+
+ // Set up the database to remain alive-but-inert until all of its background activity finishes and all
+ // database connections confirm that they have closed.
+ m_hardClosedForUserDelete = true;
+ m_hardCloseProtector = this;
+
+ // Have the database unconditionally delete itself on the database task queue.
+ postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performUnconditionalDeleteBackingStore));
+
+ // Remove the database from the IDBServer's set of open databases.
+ // If there is no in-progress background thread activity for this database, it will be deleted here.
+ m_server.closeUniqueIDBDatabase(*this);
+}
+
+void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error)
+{
+ auto callback = m_errorCallbacks.take(callbackIdentifier);
+ ASSERT(callback || m_hardClosedForUserDelete);
+ if (callback)
+ callback(error);
+}
+
+void UniqueIDBDatabase::performKeyDataCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey)
+{
+ auto callback = m_keyDataCallbacks.take(callbackIdentifier);
+ ASSERT(callback || m_hardClosedForUserDelete);
+ if (callback)
+ callback(error, resultKey);
+}
+
+void UniqueIDBDatabase::performGetResultCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBGetResult& resultData)
+{
+ auto callback = m_getResultCallbacks.take(callbackIdentifier);
+ ASSERT(callback || m_hardClosedForUserDelete);
+ if (callback)
+ callback(error, resultData);
+}
+
+void UniqueIDBDatabase::performGetAllResultsCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBGetAllResult& resultData)
+{
+ auto callback = m_getAllResultsCallbacks.take(callbackIdentifier);
+ ASSERT(callback || m_hardClosedForUserDelete);
+ if (callback)
+ callback(error, resultData);
+}
+
+void UniqueIDBDatabase::performCountCallback(uint64_t callbackIdentifier, const IDBError& error, uint64_t count)
+{
+ auto callback = m_countCallbacks.take(callbackIdentifier);
+ ASSERT(callback || m_hardClosedForUserDelete);
+ if (callback)
+ callback(error, count);
+}
+
+void UniqueIDBDatabase::forgetErrorCallback(uint64_t callbackIdentifier)
+{
+ ASSERT(m_errorCallbacks.contains(callbackIdentifier));
+ m_errorCallbacks.remove(callbackIdentifier);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h
new file mode 100644
index 000000000..93edced5c
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBBackingStore.h"
+#include "IDBBindingUtilities.h"
+#include "IDBDatabaseIdentifier.h"
+#include "IDBDatabaseInfo.h"
+#include "IDBGetResult.h"
+#include "ServerOpenDBRequest.h"
+#include "ThreadSafeDataBuffer.h"
+#include "Timer.h"
+#include "UniqueIDBDatabaseConnection.h"
+#include "UniqueIDBDatabaseTransaction.h"
+#include <wtf/CrossThreadQueue.h>
+#include <wtf/CrossThreadTask.h>
+#include <wtf/Deque.h>
+#include <wtf/HashCountedSet.h>
+#include <wtf/HashSet.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace JSC {
+class VM;
+}
+
+namespace WebCore {
+
+class IDBError;
+class IDBGetAllResult;
+class IDBRequestData;
+class IDBTransactionInfo;
+
+enum class IDBGetRecordDataType;
+
+namespace IndexedDB {
+enum class IndexRecordType;
+}
+
+namespace IDBServer {
+
+class IDBConnectionToClient;
+class IDBServer;
+
+typedef std::function<void(const IDBError&)> ErrorCallback;
+typedef std::function<void(const IDBError&, const IDBKeyData&)> KeyDataCallback;
+typedef std::function<void(const IDBError&, const IDBGetResult&)> GetResultCallback;
+typedef std::function<void(const IDBError&, const IDBGetAllResult&)> GetAllResultsCallback;
+typedef std::function<void(const IDBError&, uint64_t)> CountCallback;
+
+class UniqueIDBDatabase : public ThreadSafeRefCounted<UniqueIDBDatabase> {
+public:
+ static Ref<UniqueIDBDatabase> create(IDBServer& server, const IDBDatabaseIdentifier& identifier)
+ {
+ return adoptRef(*new UniqueIDBDatabase(server, identifier));
+ }
+
+ WEBCORE_EXPORT ~UniqueIDBDatabase();
+
+ void openDatabaseConnection(IDBConnectionToClient&, const IDBRequestData&);
+
+ const IDBDatabaseInfo& info() const;
+ IDBServer& server() { return m_server; }
+ const IDBDatabaseIdentifier& identifier() const { return m_identifier; }
+
+ void createObjectStore(UniqueIDBDatabaseTransaction&, const IDBObjectStoreInfo&, ErrorCallback);
+ void deleteObjectStore(UniqueIDBDatabaseTransaction&, const String& objectStoreName, ErrorCallback);
+ void renameObjectStore(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback);
+ void clearObjectStore(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, ErrorCallback);
+ void createIndex(UniqueIDBDatabaseTransaction&, const IDBIndexInfo&, ErrorCallback);
+ void deleteIndex(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, const String& indexName, ErrorCallback);
+ void renameIndex(UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback);
+ void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode, KeyDataCallback);
+ void getRecord(const IDBRequestData&, const IDBGetRecordData&, GetResultCallback);
+ void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&, GetAllResultsCallback);
+ void getCount(const IDBRequestData&, const IDBKeyRangeData&, CountCallback);
+ void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&, ErrorCallback);
+ void openCursor(const IDBRequestData&, const IDBCursorInfo&, GetResultCallback);
+ void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&, GetResultCallback);
+ void commitTransaction(UniqueIDBDatabaseTransaction&, ErrorCallback);
+ void abortTransaction(UniqueIDBDatabaseTransaction&, ErrorCallback);
+ void didFinishHandlingVersionChange(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& transactionIdentifier);
+ void transactionDestroyed(UniqueIDBDatabaseTransaction&);
+ void connectionClosedFromClient(UniqueIDBDatabaseConnection&);
+ void confirmConnectionClosedOnServer(UniqueIDBDatabaseConnection&);
+ void didFireVersionChangeEvent(UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier);
+ void openDBRequestCancelled(const IDBResourceIdentifier& requestIdentifier);
+ void confirmDidCloseFromServer(UniqueIDBDatabaseConnection&);
+
+ void enqueueTransaction(Ref<UniqueIDBDatabaseTransaction>&&);
+
+ void handleDelete(IDBConnectionToClient&, const IDBRequestData&);
+ void immediateCloseForUserDelete();
+
+ static JSC::VM& databaseThreadVM();
+ static JSC::ExecState& databaseThreadExecState();
+
+ bool hardClosedForUserDelete() const { return m_hardClosedForUserDelete; }
+
+private:
+ UniqueIDBDatabase(IDBServer&, const IDBDatabaseIdentifier&);
+
+ void handleDatabaseOperations();
+ void handleCurrentOperation();
+ void performCurrentOpenOperation();
+ void performCurrentDeleteOperation();
+ void addOpenDatabaseConnection(Ref<UniqueIDBDatabaseConnection>&&);
+ bool hasAnyOpenConnections() const;
+ bool allConnectionsAreClosedOrClosing() const;
+
+ void startVersionChangeTransaction();
+ void maybeNotifyConnectionsOfVersionChange();
+ void notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier);
+ bool isVersionChangeInProgress();
+
+ void activateTransactionInBackingStore(UniqueIDBDatabaseTransaction&);
+ void transactionCompleted(RefPtr<UniqueIDBDatabaseTransaction>&&);
+
+ void connectionClosedFromServer(UniqueIDBDatabaseConnection&);
+
+ // Database thread operations
+ void deleteBackingStore(const IDBDatabaseIdentifier&);
+ void openBackingStore(const IDBDatabaseIdentifier&);
+ void performCommitTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier);
+ void performAbortTransaction(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier);
+ void beginTransactionInBackingStore(const IDBTransactionInfo&);
+ void performCreateObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&);
+ void performDeleteObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier);
+ void performRenameObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const String& newName);
+ void performClearObjectStore(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier);
+ void performCreateIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBIndexInfo&);
+ void performDeleteIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier);
+ void performRenameIndex(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName);
+ void performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode);
+ void performGetRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType);
+ void performGetAllRecords(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&);
+ void performGetIndexRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&);
+ void performGetCount(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&);
+ void performDeleteRecord(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&);
+ void performOpenCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&);
+ void performIterateCursor(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&);
+ void performPrefetchCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier);
+
+ void performActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBTransactionInfo&);
+ void performUnconditionalDeleteBackingStore();
+
+ // Main thread callbacks
+ void didDeleteBackingStore(uint64_t deletedVersion);
+ void didOpenBackingStore(const IDBDatabaseInfo&, const IDBError&);
+ void didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError&, const IDBObjectStoreInfo&);
+ void didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier);
+ void didPerformRenameObjectStore(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier, const String& newName);
+ void didPerformClearObjectStore(uint64_t callbackIdentifier, const IDBError&);
+ void didPerformCreateIndex(uint64_t callbackIdentifier, const IDBError&, const IDBIndexInfo&);
+ void didPerformDeleteIndex(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier);
+ void didPerformRenameIndex(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName);
+ void didPerformPutOrAdd(uint64_t callbackIdentifier, const IDBError&, const IDBKeyData&);
+ void didPerformGetRecord(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&);
+ void didPerformGetAllRecords(uint64_t callbackIdentifier, const IDBError&, const IDBGetAllResult&);
+ void didPerformGetCount(uint64_t callbackIdentifier, const IDBError&, uint64_t);
+ void didPerformDeleteRecord(uint64_t callbackIdentifier, const IDBError&);
+ void didPerformOpenCursor(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&);
+ void didPerformIterateCursor(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&);
+ void didPerformCommitTransaction(uint64_t callbackIdentifier, const IDBError&, const IDBResourceIdentifier& transactionIdentifier);
+ void didPerformAbortTransaction(uint64_t callbackIdentifier, const IDBError&, const IDBResourceIdentifier& transactionIdentifier);
+ void didPerformActivateTransactionInBackingStore(uint64_t callbackIdentifier, const IDBError&);
+ void didPerformUnconditionalDeleteBackingStore();
+
+ uint64_t storeCallbackOrFireError(ErrorCallback);
+ uint64_t storeCallbackOrFireError(KeyDataCallback);
+ uint64_t storeCallbackOrFireError(GetAllResultsCallback);
+ uint64_t storeCallbackOrFireError(GetResultCallback);
+ uint64_t storeCallbackOrFireError(CountCallback);
+
+ void performErrorCallback(uint64_t callbackIdentifier, const IDBError&);
+ void performKeyDataCallback(uint64_t callbackIdentifier, const IDBError&, const IDBKeyData&);
+ void performGetResultCallback(uint64_t callbackIdentifier, const IDBError&, const IDBGetResult&);
+ void performGetAllResultsCallback(uint64_t callbackIdentifier, const IDBError&, const IDBGetAllResult&);
+ void performCountCallback(uint64_t callbackIdentifier, const IDBError&, uint64_t);
+
+ void forgetErrorCallback(uint64_t callbackIdentifier);
+
+ bool hasAnyPendingCallbacks() const;
+ bool isCurrentlyInUse() const;
+ bool hasUnfinishedTransactions() const;
+
+ void invokeOperationAndTransactionTimer();
+ void operationAndTransactionTimerFired();
+ RefPtr<UniqueIDBDatabaseTransaction> takeNextRunnableTransaction(bool& hadDeferredTransactions);
+
+ bool prepareToFinishTransaction(UniqueIDBDatabaseTransaction&);
+
+ void postDatabaseTask(CrossThreadTask&&);
+ void postDatabaseTaskReply(CrossThreadTask&&);
+ void executeNextDatabaseTask();
+ void executeNextDatabaseTaskReply();
+
+ void maybeFinishHardClose();
+ bool isDoneWithHardClose();
+
+ IDBServer& m_server;
+ IDBDatabaseIdentifier m_identifier;
+
+ ListHashSet<RefPtr<ServerOpenDBRequest>> m_pendingOpenDBRequests;
+ RefPtr<ServerOpenDBRequest> m_currentOpenDBRequest;
+
+ ListHashSet<RefPtr<UniqueIDBDatabaseConnection>> m_openDatabaseConnections;
+ HashSet<RefPtr<UniqueIDBDatabaseConnection>> m_clientClosePendingDatabaseConnections;
+ HashSet<RefPtr<UniqueIDBDatabaseConnection>> m_serverClosePendingDatabaseConnections;
+
+ RefPtr<UniqueIDBDatabaseConnection> m_versionChangeDatabaseConnection;
+ RefPtr<UniqueIDBDatabaseTransaction> m_versionChangeTransaction;
+
+ bool m_isOpeningBackingStore { false };
+ IDBError m_backingStoreOpenError;
+ std::unique_ptr<IDBBackingStore> m_backingStore;
+ std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
+ std::unique_ptr<IDBDatabaseInfo> m_mostRecentDeletedDatabaseInfo;
+
+ bool m_backingStoreSupportsSimultaneousTransactions { false };
+ bool m_backingStoreIsEphemeral { false };
+
+ HashMap<uint64_t, ErrorCallback> m_errorCallbacks;
+ HashMap<uint64_t, KeyDataCallback> m_keyDataCallbacks;
+ HashMap<uint64_t, GetResultCallback> m_getResultCallbacks;
+ HashMap<uint64_t, GetAllResultsCallback> m_getAllResultsCallbacks;
+ HashMap<uint64_t, CountCallback> m_countCallbacks;
+
+ Timer m_operationAndTransactionTimer;
+
+ Deque<RefPtr<UniqueIDBDatabaseTransaction>> m_pendingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<UniqueIDBDatabaseTransaction>> m_inProgressTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<UniqueIDBDatabaseTransaction>> m_finishingTransactions;
+
+ // The keys into these sets are the object store ID.
+ // These sets help to decide which transactions can be started and which must be deferred.
+ HashCountedSet<uint64_t> m_objectStoreTransactionCounts;
+ HashSet<uint64_t> m_objectStoreWriteTransactions;
+
+ bool m_deleteBackingStoreInProgress { false };
+
+ CrossThreadQueue<Function<void ()>> m_databaseQueue;
+ CrossThreadQueue<Function<void ()>> m_databaseReplyQueue;
+ std::atomic<uint64_t> m_queuedTaskCount { 0 };
+
+ bool m_hardClosedForUserDelete { false };
+ RefPtr<UniqueIDBDatabase> m_hardCloseProtector;
+
+ HashMap<IDBResourceIdentifier, RefPtr<UniqueIDBDatabase>> m_prefetchProtectors;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp
new file mode 100644
index 000000000..694b7171d
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "UniqueIDBDatabaseConnection.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToClient.h"
+#include "IDBServer.h"
+#include "IDBTransactionInfo.h"
+#include "Logging.h"
+#include "ServerOpenDBRequest.h"
+#include "UniqueIDBDatabase.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+static uint64_t nextDatabaseConnectionIdentifier()
+{
+ static uint64_t nextIdentifier = 0;
+ return ++nextIdentifier;
+}
+
+Ref<UniqueIDBDatabaseConnection> UniqueIDBDatabaseConnection::create(UniqueIDBDatabase& database, ServerOpenDBRequest& request)
+{
+ return adoptRef(*new UniqueIDBDatabaseConnection(database, request));
+}
+
+UniqueIDBDatabaseConnection::UniqueIDBDatabaseConnection(UniqueIDBDatabase& database, ServerOpenDBRequest& request)
+ : m_identifier(nextDatabaseConnectionIdentifier())
+ , m_database(database)
+ , m_connectionToClient(request.connection())
+ , m_openRequestIdentifier(request.requestData().requestIdentifier())
+{
+ m_database.server().registerDatabaseConnection(*this);
+ m_connectionToClient.registerDatabaseConnection(*this);
+}
+
+UniqueIDBDatabaseConnection::~UniqueIDBDatabaseConnection()
+{
+ m_database.server().unregisterDatabaseConnection(*this);
+ m_connectionToClient.unregisterDatabaseConnection(*this);
+}
+
+bool UniqueIDBDatabaseConnection::hasNonFinishedTransactions() const
+{
+ return !m_transactionMap.isEmpty();
+}
+
+void UniqueIDBDatabaseConnection::abortTransactionWithoutCallback(UniqueIDBDatabaseTransaction& transaction)
+{
+ ASSERT(m_transactionMap.contains(transaction.info().identifier()));
+
+ const auto& transactionIdentifier = transaction.info().identifier();
+ RefPtr<UniqueIDBDatabaseConnection> protectedThis(this);
+
+ m_database.abortTransaction(transaction, [this, protectedThis, transactionIdentifier](const IDBError&) {
+ ASSERT(m_transactionMap.contains(transactionIdentifier));
+ m_transactionMap.remove(transactionIdentifier);
+ });
+}
+
+void UniqueIDBDatabaseConnection::connectionPendingCloseFromClient()
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::connectionPendingCloseFromClient - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+
+ m_closePending = true;
+}
+
+void UniqueIDBDatabaseConnection::connectionClosedFromClient()
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::connectionClosedFromClient - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+
+ m_database.connectionClosedFromClient(*this);
+}
+
+void UniqueIDBDatabaseConnection::confirmDidCloseFromServer()
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::confirmDidCloseFromServer - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+
+ m_database.confirmDidCloseFromServer(*this);
+}
+
+void UniqueIDBDatabaseConnection::didFireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didFireVersionChangeEvent - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+
+ m_database.didFireVersionChangeEvent(*this, requestIdentifier);
+}
+
+void UniqueIDBDatabaseConnection::didFinishHandlingVersionChange(const IDBResourceIdentifier& transactionIdentifier)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didFinishHandlingVersionChange - %s - %" PRIu64, transactionIdentifier.loggingString().utf8().data(), m_identifier);
+
+ m_database.didFinishHandlingVersionChange(*this, transactionIdentifier);
+}
+
+void UniqueIDBDatabaseConnection::fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion)
+{
+ ASSERT(!m_closePending);
+ m_connectionToClient.fireVersionChangeEvent(*this, requestIdentifier, requestedVersion);
+}
+
+UniqueIDBDatabaseTransaction& UniqueIDBDatabaseConnection::createVersionChangeTransaction(uint64_t newVersion)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::createVersionChangeTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+ ASSERT(!m_closePending);
+
+ IDBTransactionInfo info = IDBTransactionInfo::versionChange(m_connectionToClient, m_database.info(), newVersion);
+
+ Ref<UniqueIDBDatabaseTransaction> transaction = UniqueIDBDatabaseTransaction::create(*this, info);
+ m_transactionMap.set(transaction->info().identifier(), &transaction.get());
+
+ return transaction.get();
+}
+
+void UniqueIDBDatabaseConnection::establishTransaction(const IDBTransactionInfo& info)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::establishTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+
+ ASSERT(info.mode() != IDBTransactionMode::Versionchange);
+
+ // No transactions should ever come from the client after the client has already told us
+ // the connection is closing.
+ ASSERT(!m_closePending);
+
+ Ref<UniqueIDBDatabaseTransaction> transaction = UniqueIDBDatabaseTransaction::create(*this, info);
+ m_transactionMap.set(transaction->info().identifier(), &transaction.get());
+ m_database.enqueueTransaction(WTFMove(transaction));
+}
+
+void UniqueIDBDatabaseConnection::didAbortTransaction(UniqueIDBDatabaseTransaction& transaction, const IDBError& error)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didAbortTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+
+ auto transactionIdentifier = transaction.info().identifier();
+ auto takenTransaction = m_transactionMap.take(transactionIdentifier);
+
+ ASSERT(takenTransaction || m_database.hardClosedForUserDelete());
+ if (takenTransaction)
+ m_connectionToClient.didAbortTransaction(transactionIdentifier, error);
+}
+
+void UniqueIDBDatabaseConnection::didCommitTransaction(UniqueIDBDatabaseTransaction& transaction, const IDBError& error)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didCommitTransaction - %s - %" PRIu64, m_openRequestIdentifier.loggingString().utf8().data(), m_identifier);
+
+ auto transactionIdentifier = transaction.info().identifier();
+
+ ASSERT(m_transactionMap.contains(transactionIdentifier));
+ m_transactionMap.remove(transactionIdentifier);
+
+ m_connectionToClient.didCommitTransaction(transactionIdentifier, error);
+}
+
+void UniqueIDBDatabaseConnection::didCreateObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didCreateObjectStore");
+
+ m_connectionToClient.didCreateObjectStore(resultData);
+}
+
+void UniqueIDBDatabaseConnection::didDeleteObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didDeleteObjectStore");
+
+ m_connectionToClient.didDeleteObjectStore(resultData);
+}
+
+void UniqueIDBDatabaseConnection::didRenameObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didRenameObjectStore");
+
+ m_connectionToClient.didRenameObjectStore(resultData);
+}
+
+void UniqueIDBDatabaseConnection::didClearObjectStore(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didClearObjectStore");
+
+ m_connectionToClient.didClearObjectStore(resultData);
+}
+
+void UniqueIDBDatabaseConnection::didCreateIndex(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didCreateIndex");
+
+ m_connectionToClient.didCreateIndex(resultData);
+}
+
+void UniqueIDBDatabaseConnection::didDeleteIndex(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didDeleteIndex");
+
+ m_connectionToClient.didDeleteIndex(resultData);
+}
+
+void UniqueIDBDatabaseConnection::didRenameIndex(const IDBResultData& resultData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseConnection::didRenameIndex");
+
+ m_connectionToClient.didRenameIndex(resultData);
+}
+
+bool UniqueIDBDatabaseConnection::connectionIsClosing() const
+{
+ return m_closePending;
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h
new file mode 100644
index 000000000..27d5884b3
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "UniqueIDBDatabaseTransaction.h"
+#include <wtf/HashMap.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBError;
+class IDBResultData;
+
+namespace IDBServer {
+
+class IDBConnectionToClient;
+class ServerOpenDBRequest;
+class UniqueIDBDatabase;
+class UniqueIDBDatabaseTransaction;
+
+class UniqueIDBDatabaseConnection : public RefCounted<UniqueIDBDatabaseConnection> {
+public:
+ static Ref<UniqueIDBDatabaseConnection> create(UniqueIDBDatabase&, ServerOpenDBRequest&);
+
+ ~UniqueIDBDatabaseConnection();
+
+ uint64_t identifier() const { return m_identifier; }
+ const IDBResourceIdentifier& openRequestIdentifier() { return m_openRequestIdentifier; }
+ UniqueIDBDatabase& database() { return m_database; }
+ IDBConnectionToClient& connectionToClient() { return m_connectionToClient; }
+
+ void connectionPendingCloseFromClient();
+ void connectionClosedFromClient();
+
+ bool closePending() const { return m_closePending; }
+
+ bool hasNonFinishedTransactions() const;
+
+ void fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion);
+ UniqueIDBDatabaseTransaction& createVersionChangeTransaction(uint64_t newVersion);
+
+ void establishTransaction(const IDBTransactionInfo&);
+ void didAbortTransaction(UniqueIDBDatabaseTransaction&, const IDBError&);
+ void didCommitTransaction(UniqueIDBDatabaseTransaction&, const IDBError&);
+ void didCreateObjectStore(const IDBResultData&);
+ void didDeleteObjectStore(const IDBResultData&);
+ void didRenameObjectStore(const IDBResultData&);
+ void didClearObjectStore(const IDBResultData&);
+ void didCreateIndex(const IDBResultData&);
+ void didDeleteIndex(const IDBResultData&);
+ void didRenameIndex(const IDBResultData&);
+ void didFireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier);
+ void didFinishHandlingVersionChange(const IDBResourceIdentifier& transactionIdentifier);
+ void confirmDidCloseFromServer();
+
+ void abortTransactionWithoutCallback(UniqueIDBDatabaseTransaction&);
+
+ bool connectionIsClosing() const;
+
+private:
+ UniqueIDBDatabaseConnection(UniqueIDBDatabase&, ServerOpenDBRequest&);
+
+ uint64_t m_identifier { 0 };
+ UniqueIDBDatabase& m_database;
+ IDBConnectionToClient& m_connectionToClient;
+ IDBResourceIdentifier m_openRequestIdentifier;
+
+ bool m_closePending { false };
+
+ HashMap<IDBResourceIdentifier, RefPtr<UniqueIDBDatabaseTransaction>> m_transactionMap;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp
new file mode 100644
index 000000000..214f8c238
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "UniqueIDBDatabaseTransaction.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBError.h"
+#include "IDBIterateCursorData.h"
+#include "IDBResultData.h"
+#include "IDBServer.h"
+#include "Logging.h"
+#include "UniqueIDBDatabase.h"
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<UniqueIDBDatabaseTransaction> UniqueIDBDatabaseTransaction::create(UniqueIDBDatabaseConnection& connection, const IDBTransactionInfo& info)
+{
+ return adoptRef(*new UniqueIDBDatabaseTransaction(connection, info));
+}
+
+UniqueIDBDatabaseTransaction::UniqueIDBDatabaseTransaction(UniqueIDBDatabaseConnection& connection, const IDBTransactionInfo& info)
+ : m_databaseConnection(connection)
+ , m_transactionInfo(info)
+{
+ if (m_transactionInfo.mode() == IDBTransactionMode::Versionchange)
+ m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(m_databaseConnection->database().info());
+
+ m_databaseConnection->database().server().registerTransaction(*this);
+}
+
+UniqueIDBDatabaseTransaction::~UniqueIDBDatabaseTransaction()
+{
+ m_databaseConnection->database().transactionDestroyed(*this);
+ m_databaseConnection->database().server().unregisterTransaction(*this);
+}
+
+IDBDatabaseInfo* UniqueIDBDatabaseTransaction::originalDatabaseInfo() const
+{
+ ASSERT(m_transactionInfo.mode() == IDBTransactionMode::Versionchange);
+ return m_originalDatabaseInfo.get();
+}
+
+void UniqueIDBDatabaseTransaction::abort()
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::abort");
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().abortTransaction(*this, [this, protectedThis](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::abort (callback)");
+ m_databaseConnection->didAbortTransaction(*this, error);
+ });
+}
+
+void UniqueIDBDatabaseTransaction::abortWithoutCallback()
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::abortWithoutCallback");
+
+ m_databaseConnection->abortTransactionWithoutCallback(*this);
+}
+
+bool UniqueIDBDatabaseTransaction::isVersionChange() const
+{
+ return m_transactionInfo.mode() == IDBTransactionMode::Versionchange;
+}
+
+bool UniqueIDBDatabaseTransaction::isReadOnly() const
+{
+ return m_transactionInfo.mode() == IDBTransactionMode::Readonly;
+}
+
+void UniqueIDBDatabaseTransaction::commit()
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::commit");
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().commitTransaction(*this, [this, protectedThis](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::commit (callback)");
+ m_databaseConnection->didCommitTransaction(*this, error);
+ });
+}
+
+void UniqueIDBDatabaseTransaction::createObjectStore(const IDBRequestData& requestData, const IDBObjectStoreInfo& info)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createObjectStore");
+
+ ASSERT(isVersionChange());
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().createObjectStore(*this, info, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createObjectStore (callback)");
+ if (error.isNull())
+ m_databaseConnection->didCreateObjectStore(IDBResultData::createObjectStoreSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->didCreateObjectStore(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteObjectStore");
+
+ ASSERT(isVersionChange());
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().deleteObjectStore(*this, objectStoreName, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteObjectStore (callback)");
+ if (error.isNull())
+ m_databaseConnection->didDeleteObjectStore(IDBResultData::deleteObjectStoreSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->didDeleteObjectStore(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameObjectStore");
+
+ ASSERT(isVersionChange());
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().renameObjectStore(*this, objectStoreIdentifier, newName, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameObjectStore (callback)");
+ if (error.isNull())
+ m_databaseConnection->didRenameObjectStore(IDBResultData::renameObjectStoreSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->didRenameObjectStore(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::clearObjectStore");
+
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().clearObjectStore(*this, objectStoreIdentifier, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::clearObjectStore (callback)");
+ if (error.isNull())
+ m_databaseConnection->didClearObjectStore(IDBResultData::clearObjectStoreSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->didClearObjectStore(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createIndex");
+
+ ASSERT(isVersionChange());
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().createIndex(*this, info, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createIndex (callback)");
+ if (error.isNull())
+ m_databaseConnection->didCreateIndex(IDBResultData::createIndexSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->didCreateIndex(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteIndex");
+
+ ASSERT(isVersionChange());
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().deleteIndex(*this, objectStoreIdentifier, indexName, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::createIndex (callback)");
+ if (error.isNull())
+ m_databaseConnection->didDeleteIndex(IDBResultData::deleteIndexSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->didDeleteIndex(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameIndex");
+
+ ASSERT(isVersionChange());
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().renameIndex(*this, objectStoreIdentifier, indexIdentifier, newName, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::renameIndex (callback)");
+ if (error.isNull())
+ m_databaseConnection->didRenameIndex(IDBResultData::renameIndexSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->didRenameIndex(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+
+void UniqueIDBDatabaseTransaction::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::putOrAdd");
+
+ ASSERT(!isReadOnly());
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().putOrAdd(requestData, keyData, value, overwriteMode, [this, protectedThis, requestData](const IDBError& error, const IDBKeyData& key) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::putOrAdd (callback)");
+
+ if (error.isNull())
+ m_databaseConnection->connectionToClient().didPutOrAdd(IDBResultData::putOrAddSuccess(requestData.requestIdentifier(), key));
+ else
+ m_databaseConnection->connectionToClient().didPutOrAdd(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getRecord");
+
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().getRecord(requestData, getRecordData, [this, protectedThis, requestData](const IDBError& error, const IDBGetResult& result) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getRecord (callback)");
+
+ if (error.isNull())
+ m_databaseConnection->connectionToClient().didGetRecord(IDBResultData::getRecordSuccess(requestData.requestIdentifier(), result));
+ else
+ m_databaseConnection->connectionToClient().didGetRecord(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getAllRecords");
+
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().getAllRecords(requestData, getAllRecordsData, [this, protectedThis, requestData](const IDBError& error, const IDBGetAllResult& result) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getAllRecords (callback)");
+
+ if (error.isNull())
+ m_databaseConnection->connectionToClient().didGetAllRecords(IDBResultData::getAllRecordsSuccess(requestData.requestIdentifier(), result));
+ else
+ m_databaseConnection->connectionToClient().didGetAllRecords(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getCount");
+
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().getCount(requestData, keyRangeData, [this, protectedThis, requestData](const IDBError& error, uint64_t count) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::getCount (callback)");
+
+ if (error.isNull())
+ m_databaseConnection->connectionToClient().didGetCount(IDBResultData::getCountSuccess(requestData.requestIdentifier(), count));
+ else
+ m_databaseConnection->connectionToClient().didGetCount(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteRecord");
+
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().deleteRecord(requestData, keyRangeData, [this, protectedThis, requestData](const IDBError& error) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::deleteRecord (callback)");
+
+ if (error.isNull())
+ m_databaseConnection->connectionToClient().didDeleteRecord(IDBResultData::deleteRecordSuccess(requestData.requestIdentifier()));
+ else
+ m_databaseConnection->connectionToClient().didDeleteRecord(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::openCursor");
+
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().openCursor(requestData, info, [this, protectedThis, requestData](const IDBError& error, const IDBGetResult& result) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::openCursor (callback)");
+
+ if (error.isNull())
+ m_databaseConnection->connectionToClient().didOpenCursor(IDBResultData::openCursorSuccess(requestData.requestIdentifier(), result));
+ else
+ m_databaseConnection->connectionToClient().didOpenCursor(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+void UniqueIDBDatabaseTransaction::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::iterateCursor");
+
+ ASSERT(m_transactionInfo.identifier() == requestData.transactionIdentifier());
+
+ RefPtr<UniqueIDBDatabaseTransaction> protectedThis(this);
+ m_databaseConnection->database().iterateCursor(requestData, data, [this, protectedThis, requestData](const IDBError& error, const IDBGetResult& result) {
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::iterateCursor (callback)");
+
+ if (error.isNull())
+ m_databaseConnection->connectionToClient().didIterateCursor(IDBResultData::iterateCursorSuccess(requestData.requestIdentifier(), result));
+ else
+ m_databaseConnection->connectionToClient().didIterateCursor(IDBResultData::error(requestData.requestIdentifier(), error));
+ });
+}
+
+const Vector<uint64_t>& UniqueIDBDatabaseTransaction::objectStoreIdentifiers()
+{
+ if (!m_objectStoreIdentifiers.isEmpty())
+ return m_objectStoreIdentifiers;
+
+ auto& info = m_databaseConnection->database().info();
+ for (auto objectStoreName : info.objectStoreNames()) {
+ auto objectStoreInfo = info.infoForExistingObjectStore(objectStoreName);
+ ASSERT(objectStoreInfo);
+ if (!objectStoreInfo)
+ continue;
+
+ if (m_transactionInfo.objectStores().contains(objectStoreName))
+ m_objectStoreIdentifiers.append(objectStoreInfo->identifier());
+ }
+
+ return m_objectStoreIdentifiers;
+}
+
+void UniqueIDBDatabaseTransaction::didActivateInBackingStore(const IDBError& error)
+{
+ LOG(IndexedDB, "UniqueIDBDatabaseTransaction::didActivateInBackingStore");
+
+ m_databaseConnection->connectionToClient().didStartTransaction(m_transactionInfo.identifier(), error);
+}
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h
new file mode 100644
index 000000000..511d57124
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBTransactionInfo.h"
+#include "UniqueIDBDatabaseConnection.h"
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBDatabaseInfo;
+class IDBError;
+class IDBIndexInfo;
+class IDBKeyData;
+class IDBObjectStoreInfo;
+class IDBRequestData;
+class IDBValue;
+
+struct IDBGetAllRecordsData;
+struct IDBGetRecordData;
+struct IDBIterateCursorData;
+struct IDBKeyRangeData;
+
+namespace IDBServer {
+
+class UniqueIDBDatabaseConnection;
+
+class UniqueIDBDatabaseTransaction : public RefCounted<UniqueIDBDatabaseTransaction> {
+public:
+ static Ref<UniqueIDBDatabaseTransaction> create(UniqueIDBDatabaseConnection&, const IDBTransactionInfo&);
+
+ ~UniqueIDBDatabaseTransaction();
+
+ UniqueIDBDatabaseConnection& databaseConnection() { return m_databaseConnection.get(); }
+ const IDBTransactionInfo& info() const { return m_transactionInfo; }
+ bool isVersionChange() const;
+ bool isReadOnly() const;
+
+ IDBDatabaseInfo* originalDatabaseInfo() const;
+
+ void abort();
+ void abortWithoutCallback();
+ void commit();
+
+ void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&);
+ void deleteObjectStore(const IDBRequestData&, const String& objectStoreName);
+ void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName);
+ void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier);
+ void createIndex(const IDBRequestData&, const IDBIndexInfo&);
+ void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName);
+ void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName);
+ void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode);
+ void getRecord(const IDBRequestData&, const IDBGetRecordData&);
+ void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&);
+ void getCount(const IDBRequestData&, const IDBKeyRangeData&);
+ void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&);
+ void openCursor(const IDBRequestData&, const IDBCursorInfo&);
+ void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&);
+
+ void didActivateInBackingStore(const IDBError&);
+
+ const Vector<uint64_t>& objectStoreIdentifiers();
+
+private:
+ UniqueIDBDatabaseTransaction(UniqueIDBDatabaseConnection&, const IDBTransactionInfo&);
+
+ Ref<UniqueIDBDatabaseConnection> m_databaseConnection;
+ IDBTransactionInfo m_transactionInfo;
+
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
+
+ Vector<uint64_t> m_objectStoreIdentifiers;
+};
+
+} // namespace IDBServer
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp
new file mode 100644
index 000000000..0d3e3388f
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBCursorInfo.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBDatabase.h"
+#include "IDBTransaction.h"
+#include "IndexedDB.h"
+
+namespace WebCore {
+
+IDBCursorInfo IDBCursorInfo::objectStoreCursor(IDBTransaction& transaction, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+{
+ return { transaction, objectStoreIdentifier, range, direction, type };
+}
+
+IDBCursorInfo IDBCursorInfo::indexCursor(IDBTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+{
+ return { transaction, objectStoreIdentifier, indexIdentifier, range, direction, type };
+}
+
+IDBCursorInfo::IDBCursorInfo()
+{
+}
+
+IDBCursorInfo::IDBCursorInfo(IDBTransaction& transaction, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+ : m_cursorIdentifier(transaction.database().connectionProxy())
+ , m_transactionIdentifier(transaction.info().identifier())
+ , m_objectStoreIdentifier(objectStoreIdentifier)
+ , m_sourceIdentifier(objectStoreIdentifier)
+ , m_range(range)
+ , m_source(IndexedDB::CursorSource::ObjectStore)
+ , m_direction(direction)
+ , m_type(type)
+{
+}
+
+IDBCursorInfo::IDBCursorInfo(IDBTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+ : m_cursorIdentifier(transaction.database().connectionProxy())
+ , m_transactionIdentifier(transaction.info().identifier())
+ , m_objectStoreIdentifier(objectStoreIdentifier)
+ , m_sourceIdentifier(indexIdentifier)
+ , m_range(range)
+ , m_source(IndexedDB::CursorSource::Index)
+ , m_direction(direction)
+ , m_type(type)
+{
+}
+
+IDBCursorInfo::IDBCursorInfo(const IDBResourceIdentifier& cursorIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t sourceIdentifier, const IDBKeyRangeData& range, IndexedDB::CursorSource source, IndexedDB::CursorDirection direction, IndexedDB::CursorType type)
+ : m_cursorIdentifier(cursorIdentifier)
+ , m_transactionIdentifier(transactionIdentifier)
+ , m_objectStoreIdentifier(objectStoreIdentifier)
+ , m_sourceIdentifier(sourceIdentifier)
+ , m_range(range)
+ , m_source(source)
+ , m_direction(direction)
+ , m_type(type)
+{
+}
+
+bool IDBCursorInfo::isDirectionForward() const
+{
+ return m_direction == IndexedDB::CursorDirection::Next || m_direction == IndexedDB::CursorDirection::Nextunique;
+}
+
+CursorDuplicity IDBCursorInfo::duplicity() const
+{
+ return m_direction == IndexedDB::CursorDirection::Nextunique || m_direction == IndexedDB::CursorDirection::Prevunique ? CursorDuplicity::NoDuplicates : CursorDuplicity::Duplicates;
+}
+
+IDBCursorInfo IDBCursorInfo::isolatedCopy() const
+{
+ return { m_cursorIdentifier.isolatedCopy(), m_transactionIdentifier.isolatedCopy(), m_objectStoreIdentifier, m_sourceIdentifier, m_range.isolatedCopy(), m_source, m_direction, m_type };
+}
+
+#if !LOG_DISABLED
+String IDBCursorInfo::loggingString() const
+{
+ if (m_source == IndexedDB::CursorSource::Index)
+ return String::format("<Crsr: %s Idx %" PRIu64 ", OS %" PRIu64 ", tx %s>", m_cursorIdentifier.loggingString().utf8().data(), m_sourceIdentifier, m_objectStoreIdentifier, m_transactionIdentifier.loggingString().utf8().data());
+
+ return String::format("<Crsr: %s OS %" PRIu64 ", tx %s>", m_cursorIdentifier.loggingString().utf8().data(), m_objectStoreIdentifier, m_transactionIdentifier.loggingString().utf8().data());
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h
new file mode 100644
index 000000000..84b439be3
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyRangeData.h"
+#include "IDBResourceIdentifier.h"
+
+namespace WebCore {
+
+class IDBTransaction;
+
+namespace IndexedDB {
+enum class CursorDirection;
+enum class CursorSource;
+enum class CursorType;
+}
+
+struct IDBKeyRangeData;
+
+enum class CursorDuplicity {
+ Duplicates,
+ NoDuplicates,
+};
+
+class IDBCursorInfo {
+public:
+ static IDBCursorInfo objectStoreCursor(IDBTransaction&, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
+ static IDBCursorInfo indexCursor(IDBTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
+
+ IDBResourceIdentifier identifier() const { return m_cursorIdentifier; }
+ uint64_t sourceIdentifier() const { return m_sourceIdentifier; }
+ uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; }
+
+ IndexedDB::CursorSource cursorSource() const { return m_source; }
+ IndexedDB::CursorDirection cursorDirection() const { return m_direction; }
+ IndexedDB::CursorType cursorType() const { return m_type; }
+ const IDBKeyRangeData& range() const { return m_range; }
+
+ bool isDirectionForward() const;
+ CursorDuplicity duplicity() const;
+
+ IDBCursorInfo isolatedCopy() const;
+
+ WEBCORE_EXPORT IDBCursorInfo();
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBCursorInfo&);
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+private:
+ IDBCursorInfo(IDBTransaction&, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
+ IDBCursorInfo(IDBTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const IDBKeyRangeData&, IndexedDB::CursorDirection, IndexedDB::CursorType);
+
+ IDBCursorInfo(const IDBResourceIdentifier&, const IDBResourceIdentifier&, uint64_t, uint64_t, const IDBKeyRangeData&, IndexedDB::CursorSource, IndexedDB::CursorDirection, IndexedDB::CursorType);
+
+ IDBResourceIdentifier m_cursorIdentifier;
+ IDBResourceIdentifier m_transactionIdentifier;
+ uint64_t m_objectStoreIdentifier { 0 };
+ uint64_t m_sourceIdentifier { 0 };
+
+ IDBKeyRangeData m_range;
+
+ IndexedDB::CursorSource m_source;
+ IndexedDB::CursorDirection m_direction;
+ IndexedDB::CursorType m_type;
+};
+
+template<class Encoder>
+void IDBCursorInfo::encode(Encoder& encoder) const
+{
+ encoder << m_cursorIdentifier << m_transactionIdentifier << m_objectStoreIdentifier << m_sourceIdentifier << m_range;
+
+ encoder.encodeEnum(m_source);
+ encoder.encodeEnum(m_direction);
+ encoder.encodeEnum(m_type);
+}
+
+template<class Decoder>
+bool IDBCursorInfo::decode(Decoder& decoder, IDBCursorInfo& info)
+{
+ if (!decoder.decode(info.m_cursorIdentifier))
+ return false;
+
+ if (!decoder.decode(info.m_transactionIdentifier))
+ return false;
+
+ if (!decoder.decode(info.m_objectStoreIdentifier))
+ return false;
+
+ if (!decoder.decode(info.m_sourceIdentifier))
+ return false;
+
+ if (!decoder.decode(info.m_range))
+ return false;
+
+ if (!decoder.decodeEnum(info.m_source))
+ return false;
+
+ if (!decoder.decodeEnum(info.m_direction))
+ return false;
+
+ if (!decoder.decodeEnum(info.m_type))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h b/Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h
new file mode 100644
index 000000000..b4f55fd66
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyData.h"
+#include "IDBValue.h"
+
+namespace WebCore {
+
+struct IDBCursorRecord {
+ IDBKeyData key;
+ IDBKeyData primaryKey;
+ std::unique_ptr<IDBValue> value;
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBCursorRecord&);
+};
+
+template<class Encoder>
+void IDBCursorRecord::encode(Encoder& encoder) const
+{
+ encoder << key << primaryKey << value;
+}
+
+template<class Decoder>
+bool IDBCursorRecord::decode(Decoder& decoder, IDBCursorRecord& record)
+{
+ if (!decoder.decode(record.key))
+ return false;
+
+ if (!decoder.decode(record.primaryKey))
+ return false;
+
+ if (!decoder.decode(record.value))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp
new file mode 100644
index 000000000..e5df15c44
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBDatabaseInfo.h"
+
+#include <wtf/text/StringBuilder.h>
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBDatabaseInfo::IDBDatabaseInfo()
+{
+}
+
+IDBDatabaseInfo::IDBDatabaseInfo(const String& name, uint64_t version)
+ : m_name(name)
+ , m_version(version)
+{
+}
+
+IDBDatabaseInfo::IDBDatabaseInfo(const IDBDatabaseInfo& other, IsolatedCopyTag)
+ : m_name(other.m_name.isolatedCopy())
+ , m_version(other.m_version)
+ , m_maxObjectStoreID(other.m_maxObjectStoreID)
+{
+ for (auto entry : other.m_objectStoreMap)
+ m_objectStoreMap.set(entry.key, entry.value.isolatedCopy());
+}
+
+IDBDatabaseInfo IDBDatabaseInfo::isolatedCopy() const
+{
+ return { *this, IDBDatabaseInfo::IsolatedCopy };
+}
+
+bool IDBDatabaseInfo::hasObjectStore(const String& name) const
+{
+ for (auto& objectStore : m_objectStoreMap.values()) {
+ if (objectStore.name() == name)
+ return true;
+ }
+
+ return false;
+}
+
+IDBObjectStoreInfo IDBDatabaseInfo::createNewObjectStore(const String& name, std::optional<IDBKeyPath>&& keyPath, bool autoIncrement)
+{
+ IDBObjectStoreInfo info(++m_maxObjectStoreID, name, WTFMove(keyPath), autoIncrement);
+ m_objectStoreMap.set(info.identifier(), info);
+ return info;
+}
+
+void IDBDatabaseInfo::addExistingObjectStore(const IDBObjectStoreInfo& info)
+{
+ ASSERT(!m_objectStoreMap.contains(info.identifier()));
+
+ if (info.identifier() > m_maxObjectStoreID)
+ m_maxObjectStoreID = info.identifier();
+
+ m_objectStoreMap.set(info.identifier(), info);
+}
+
+IDBObjectStoreInfo* IDBDatabaseInfo::getInfoForExistingObjectStore(uint64_t objectStoreIdentifier)
+{
+ auto iterator = m_objectStoreMap.find(objectStoreIdentifier);
+ if (iterator == m_objectStoreMap.end())
+ return nullptr;
+
+ return &iterator->value;
+}
+
+IDBObjectStoreInfo* IDBDatabaseInfo::getInfoForExistingObjectStore(const String& name)
+{
+ for (auto& objectStore : m_objectStoreMap.values()) {
+ if (objectStore.name() == name)
+ return &objectStore;
+ }
+
+ return nullptr;
+}
+
+const IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(uint64_t objectStoreIdentifier) const
+{
+ return const_cast<IDBDatabaseInfo*>(this)->getInfoForExistingObjectStore(objectStoreIdentifier);
+}
+
+IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(uint64_t objectStoreIdentifier)
+{
+ return getInfoForExistingObjectStore(objectStoreIdentifier);
+}
+
+const IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(const String& name) const
+{
+ return const_cast<IDBDatabaseInfo*>(this)->getInfoForExistingObjectStore(name);
+}
+
+IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(const String& name)
+{
+ return getInfoForExistingObjectStore(name);
+}
+
+void IDBDatabaseInfo::renameObjectStore(uint64_t objectStoreIdentifier, const String& newName)
+{
+ auto* info = infoForExistingObjectStore(objectStoreIdentifier);
+ if (!info)
+ return;
+
+ info->rename(newName);
+}
+
+Vector<String> IDBDatabaseInfo::objectStoreNames() const
+{
+ Vector<String> names;
+ names.reserveCapacity(m_objectStoreMap.size());
+ for (auto& objectStore : m_objectStoreMap.values())
+ names.uncheckedAppend(objectStore.name());
+
+ return names;
+}
+
+void IDBDatabaseInfo::deleteObjectStore(const String& objectStoreName)
+{
+ auto* info = infoForExistingObjectStore(objectStoreName);
+ if (!info)
+ return;
+
+ m_objectStoreMap.remove(info->identifier());
+}
+
+void IDBDatabaseInfo::deleteObjectStore(uint64_t objectStoreIdentifier)
+{
+ m_objectStoreMap.remove(objectStoreIdentifier);
+}
+
+#if !LOG_DISABLED
+String IDBDatabaseInfo::loggingString() const
+{
+ StringBuilder builder;
+ builder.appendLiteral("Database:");
+ builder.append(m_name);
+ builder.appendLiteral(" version ");
+ builder.appendNumber(m_version);
+ builder.append('\n');
+ for (auto objectStore : m_objectStoreMap.values()) {
+ builder.append(objectStore.loggingString(1));
+ builder.append('\n');
+ }
+
+ return builder.toString();
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h
new file mode 100644
index 000000000..db8ff2c77
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBObjectStoreInfo.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class IDBDatabaseInfo {
+public:
+ IDBDatabaseInfo(const String& name, uint64_t version);
+
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBDatabaseInfo(const IDBDatabaseInfo&, IsolatedCopyTag);
+
+ IDBDatabaseInfo isolatedCopy() const;
+
+ const String& name() const { return m_name; }
+
+ void setVersion(uint64_t version) { m_version = version; }
+ uint64_t version() const { return m_version; }
+
+ bool hasObjectStore(const String& name) const;
+ IDBObjectStoreInfo createNewObjectStore(const String& name, std::optional<IDBKeyPath>&&, bool autoIncrement);
+ void addExistingObjectStore(const IDBObjectStoreInfo&);
+ IDBObjectStoreInfo* infoForExistingObjectStore(uint64_t objectStoreIdentifier);
+ IDBObjectStoreInfo* infoForExistingObjectStore(const String& objectStoreName);
+ const IDBObjectStoreInfo* infoForExistingObjectStore(uint64_t objectStoreIdentifier) const;
+ const IDBObjectStoreInfo* infoForExistingObjectStore(const String& objectStoreName) const;
+
+ void renameObjectStore(uint64_t objectStoreIdentifier, const String& newName);
+
+ Vector<String> objectStoreNames() const;
+
+ void deleteObjectStore(const String& objectStoreName);
+ void deleteObjectStore(uint64_t objectStoreIdentifier);
+
+ WEBCORE_EXPORT IDBDatabaseInfo();
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBDatabaseInfo&);
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+private:
+ IDBObjectStoreInfo* getInfoForExistingObjectStore(const String& objectStoreName);
+ IDBObjectStoreInfo* getInfoForExistingObjectStore(uint64_t objectStoreIdentifier);
+
+ String m_name;
+ uint64_t m_version { 0 };
+ uint64_t m_maxObjectStoreID { 0 };
+
+ HashMap<uint64_t, IDBObjectStoreInfo> m_objectStoreMap;
+
+};
+
+template<class Encoder>
+void IDBDatabaseInfo::encode(Encoder& encoder) const
+{
+ encoder << m_name << m_version << m_maxObjectStoreID << m_objectStoreMap;
+}
+
+template<class Decoder>
+bool IDBDatabaseInfo::decode(Decoder& decoder, IDBDatabaseInfo& info)
+{
+ if (!decoder.decode(info.m_name))
+ return false;
+
+ if (!decoder.decode(info.m_version))
+ return false;
+
+ if (!decoder.decode(info.m_maxObjectStoreID))
+ return false;
+
+ if (!decoder.decode(info.m_objectStoreMap))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBError.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBError.cpp
new file mode 100644
index 000000000..7da9486b1
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBError.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "IDBError.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBError::IDBError(ExceptionCode code)
+ : IDBError(code, emptyString())
+{
+}
+
+IDBError::IDBError(ExceptionCode code, const String& message)
+ : m_code(code)
+ , m_message(message)
+{
+}
+
+IDBError IDBError::isolatedCopy() const
+{
+ return { m_code, m_message.isolatedCopy() };
+}
+
+IDBError& IDBError::operator=(const IDBError& other)
+{
+ m_code = other.m_code;
+ m_message = other.m_message;
+ return *this;
+}
+
+String IDBError::name() const
+{
+ return IDBDatabaseException::getErrorName(m_code);
+}
+
+String IDBError::message() const
+{
+ return IDBDatabaseException::getErrorDescription(m_code);
+}
+
+RefPtr<DOMError> IDBError::toDOMError() const
+{
+ return DOMError::create(IDBDatabaseException::getErrorName(m_code), m_message);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBError.h b/Source/WebCore/Modules/indexeddb/shared/IDBError.h
new file mode 100644
index 000000000..bb5531bba
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBError.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "DOMError.h"
+#include "IDBDatabaseException.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class IDBError {
+public:
+ IDBError() { }
+ IDBError(ExceptionCode);
+ WEBCORE_EXPORT IDBError(ExceptionCode, const String& message);
+
+ static IDBError userDeleteError()
+ {
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Database deleted by request of the user") };
+ }
+
+ IDBError& operator=(const IDBError&);
+
+ RefPtr<DOMError> toDOMError() const;
+
+ ExceptionCode code() const { return m_code; }
+ String name() const;
+ String message() const;
+
+ bool isNull() const { return m_code == IDBDatabaseException::NoError; }
+
+ IDBError isolatedCopy() const;
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBError&);
+
+private:
+ ExceptionCode m_code { IDBDatabaseException::NoError };
+ String m_message;
+};
+
+template<class Encoder>
+void IDBError::encode(Encoder& encoder) const
+{
+ encoder << m_code << m_message;
+}
+
+template<class Decoder>
+bool IDBError::decode(Decoder& decoder, IDBError& error)
+{
+ if (!decoder.decode(error.m_code))
+ return false;
+
+ if (!decoder.decode(error.m_message))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp
new file mode 100644
index 000000000..92b6b923d
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBGetAllRecordsData.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyRangeData.h"
+
+namespace WebCore {
+
+IDBGetAllRecordsData IDBGetAllRecordsData::isolatedCopy() const
+{
+ return { keyRangeData.isolatedCopy(), getAllType, count, objectStoreIdentifier, indexIdentifier };
+}
+
+#if !LOG_DISABLED
+String IDBGetAllRecordsData::loggingString() const
+{
+ if (indexIdentifier)
+ return String::format("<GetAllRecords: Idx %" PRIu64 ", OS %" PRIu64 ", %s, range %s>", indexIdentifier, objectStoreIdentifier, getAllType == IndexedDB::GetAllType::Keys ? "Keys" : "Values", keyRangeData.loggingString().utf8().data());
+ return String::format("<GetAllRecords: OS %" PRIu64 ", %s, range %s>", objectStoreIdentifier, getAllType == IndexedDB::GetAllType::Keys ? "Keys" : "Values", keyRangeData.loggingString().utf8().data());
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h
index 193ceeada..e9b459800 100644
--- a/Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,45 +23,66 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBPendingOpenCall_h
-#define IDBPendingOpenCall_h
-
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefPtr.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "IDBKeyRangeData.h"
+#include <wtf/Optional.h>
+
namespace WebCore {
-class IDBCallbacks;
-class IDBDatabaseCallbacks;
-
-class IDBPendingOpenCall {
-public:
- static PassOwnPtr<IDBPendingOpenCall> create(IDBCallbacks& callbacks, IDBDatabaseCallbacks& databaseCallbacks, int64_t transactionId, uint64_t version)
- {
- return adoptPtr(new IDBPendingOpenCall(callbacks, databaseCallbacks, transactionId, version));
- }
- IDBCallbacks* callbacks() { return m_callbacks.get(); }
- IDBDatabaseCallbacks* databaseCallbacks() { return m_databaseCallbacks.get(); }
- uint64_t version() { return m_version; }
- int64_t transactionId() const { return m_transactionId; }
-
-private:
- IDBPendingOpenCall(IDBCallbacks& callbacks, IDBDatabaseCallbacks& databaseCallbacks, int64_t transactionId, uint64_t version)
- : m_callbacks(&callbacks)
- , m_databaseCallbacks(&databaseCallbacks)
- , m_version(version)
- , m_transactionId(transactionId)
- {
- }
- RefPtr<IDBCallbacks> m_callbacks;
- RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
- uint64_t m_version;
- const int64_t m_transactionId;
+namespace IndexedDB {
+enum class DataSource;
+enum class GetAllType;
+}
+
+struct IDBGetAllRecordsData {
+ IDBKeyRangeData keyRangeData;
+ IndexedDB::GetAllType getAllType;
+ std::optional<uint32_t> count;
+ uint64_t objectStoreIdentifier;
+ uint64_t indexIdentifier;
+
+ IDBGetAllRecordsData isolatedCopy() const;
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBGetAllRecordsData&);
};
+template<class Encoder>
+void IDBGetAllRecordsData::encode(Encoder& encoder) const
+{
+ encoder << keyRangeData;
+ encoder.encodeEnum(getAllType);
+ encoder << count << objectStoreIdentifier << indexIdentifier;
+}
+
+template<class Decoder>
+bool IDBGetAllRecordsData::decode(Decoder& decoder, IDBGetAllRecordsData& getAllRecordsData)
+{
+ if (!decoder.decode(getAllRecordsData.keyRangeData))
+ return false;
+
+ if (!decoder.decodeEnum(getAllRecordsData.getAllType))
+ return false;
+
+ if (!decoder.decode(getAllRecordsData.count))
+ return false;
+
+ if (!decoder.decode(getAllRecordsData.objectStoreIdentifier))
+ return false;
+
+ if (!decoder.decode(getAllRecordsData.indexIdentifier))
+ return false;
+
+ return true;
+}
+
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBPendingOpenCall_h
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp
new file mode 100644
index 000000000..d78962604
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBGetRecordData.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyRangeData.h"
+
+namespace WebCore {
+
+IDBGetRecordData IDBGetRecordData::isolatedCopy() const
+{
+ return { keyRangeData.isolatedCopy(), type };
+}
+
+#if !LOG_DISABLED
+String IDBGetRecordData::loggingString() const
+{
+ return String::format("<GetRecord: %s %s>", type == IDBGetRecordDataType::KeyOnly ? "KeyOnly" : "Key+Value", keyRangeData.loggingString().utf8().data());
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h
index c7f7bc494..e82ab54e8 100644
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,49 +23,52 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBBackingStoreTransactionLevelDB_h
-#define IDBBackingStoreTransactionLevelDB_h
-
-#include "LevelDBTransaction.h"
+#pragma once
#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
+
+#include "IDBKeyRangeData.h"
namespace WebCore {
-class IDBBackingStoreLevelDB;
+enum class IDBGetRecordDataType {
+ KeyOnly,
+ KeyAndValue,
+};
+
+struct IDBGetRecordData {
+ IDBKeyRangeData keyRangeData;
+ IDBGetRecordDataType type;
-class IDBBackingStoreTransactionLevelDB : public RefCounted<IDBBackingStoreTransactionLevelDB> {
-public:
- static PassRefPtr<IDBBackingStoreTransactionLevelDB> create(int64_t transactionID, IDBBackingStoreLevelDB* backingStore)
- {
- return adoptRef(new IDBBackingStoreTransactionLevelDB(transactionID, backingStore));
- }
+ IDBGetRecordData isolatedCopy() const;
- ~IDBBackingStoreTransactionLevelDB();
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
- void begin();
- bool commit();
- void rollback();
- void resetTransaction();
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBGetRecordData&);
+};
- static LevelDBTransaction* levelDBTransactionFrom(IDBBackingStoreTransactionLevelDB& transaction)
- {
- return static_cast<IDBBackingStoreTransactionLevelDB&>(transaction).m_transaction.get();
- }
+template<class Encoder>
+void IDBGetRecordData::encode(Encoder& encoder) const
+{
+ encoder << keyRangeData;
+ encoder.encodeEnum(type);
+}
- int64_t transactionID() { return m_transactionID; }
+template<class Decoder>
+bool IDBGetRecordData::decode(Decoder& decoder, IDBGetRecordData& getRecordData)
+{
+ if (!decoder.decode(getRecordData.keyRangeData))
+ return false;
-private:
- IDBBackingStoreTransactionLevelDB(int64_t transactionID, IDBBackingStoreLevelDB*);
+ if (!decoder.decodeEnum(getRecordData.type))
+ return false;
- int64_t m_transactionID;
- IDBBackingStoreLevelDB* m_backingStore;
- RefPtr<LevelDBTransaction> m_transaction;
-};
+ return true;
+}
} // namespace WebCore
-#endif // USE(LEVELDB)
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBBackingStoreTransactionLevelDB_h
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp
new file mode 100644
index 000000000..27290f4a4
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBIndexInfo.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBIndexInfo::IDBIndexInfo()
+{
+}
+
+IDBIndexInfo::IDBIndexInfo(uint64_t identifier, uint64_t objectStoreIdentifier, const String& name, IDBKeyPath&& keyPath, bool unique, bool multiEntry)
+ : m_identifier(identifier)
+ , m_objectStoreIdentifier(objectStoreIdentifier)
+ , m_name(name)
+ , m_keyPath(WTFMove(keyPath))
+ , m_unique(unique)
+ , m_multiEntry(multiEntry)
+{
+}
+
+IDBIndexInfo IDBIndexInfo::isolatedCopy() const
+{
+ return { m_identifier, m_objectStoreIdentifier, m_name.isolatedCopy(), WebCore::isolatedCopy(m_keyPath), m_unique, m_multiEntry };
+}
+
+#if !LOG_DISABLED
+String IDBIndexInfo::loggingString(int indent) const
+{
+ String indentString;
+ for (int i = 0; i < indent; ++i)
+ indentString.append(" ");
+
+ return makeString(indentString, "Index: ", m_name, String::format(" (%" PRIu64 ") keyPath: %s\n", m_identifier, WebCore::loggingString(m_keyPath).utf8().data()));
+}
+
+String IDBIndexInfo::condensedLoggingString() const
+{
+ return String::format("<Idx: %s (%" PRIu64 "), OS (%" PRIu64 ")>", m_name.utf8().data(), m_identifier, m_objectStoreIdentifier);
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h
new file mode 100644
index 000000000..1e3741792
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyPath.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class IDBIndexInfo {
+public:
+ WEBCORE_EXPORT IDBIndexInfo();
+ IDBIndexInfo(uint64_t identifier, uint64_t objectStoreIdentifier, const String& name, IDBKeyPath&&, bool unique, bool multiEntry);
+
+ IDBIndexInfo isolatedCopy() const;
+
+ uint64_t identifier() const { return m_identifier; }
+ uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; }
+ const String& name() const { return m_name; }
+ const IDBKeyPath& keyPath() const { return m_keyPath; }
+ bool unique() const { return m_unique; }
+ bool multiEntry() const { return m_multiEntry; }
+
+ void rename(const String& newName) { m_name = newName; }
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBIndexInfo&);
+
+#if !LOG_DISABLED
+ String loggingString(int indent = 0) const;
+ String condensedLoggingString() const;
+#endif
+
+ // FIXME: Remove the need for this.
+ static const int64_t InvalidId = -1;
+
+private:
+ uint64_t m_identifier { 0 };
+ uint64_t m_objectStoreIdentifier { 0 };
+ String m_name;
+ IDBKeyPath m_keyPath;
+ bool m_unique { true };
+ bool m_multiEntry { false };
+};
+
+template<class Encoder>
+void IDBIndexInfo::encode(Encoder& encoder) const
+{
+ encoder << m_identifier << m_objectStoreIdentifier << m_name << m_keyPath << m_unique << m_multiEntry;
+}
+
+template<class Decoder>
+bool IDBIndexInfo::decode(Decoder& decoder, IDBIndexInfo& info)
+{
+ if (!decoder.decode(info.m_identifier))
+ return false;
+
+ if (!decoder.decode(info.m_objectStoreIdentifier))
+ return false;
+
+ if (!decoder.decode(info.m_name))
+ return false;
+
+ if (!decoder.decode(info.m_keyPath))
+ return false;
+
+ if (!decoder.decode(info.m_unique))
+ return false;
+
+ if (!decoder.decode(info.m_multiEntry))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp
new file mode 100644
index 000000000..faaeda9fe
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBIterateCursorData.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBIterateCursorData IDBIterateCursorData::isolatedCopy() const
+{
+ return { keyData.isolatedCopy(), primaryKeyData.isolatedCopy(), count };
+}
+
+#if !LOG_DISABLED
+String IDBIterateCursorData::loggingString() const
+{
+ return String::format("<Itr8Crsr: key %s, primaryKey %s, count %u>", keyData.loggingString().utf8().data(), primaryKeyData.loggingString().utf8().data(), count);
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h
new file mode 100644
index 000000000..609f74876
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBKeyData.h"
+
+namespace WebCore {
+
+struct IDBIterateCursorData {
+ IDBKeyData keyData;
+ IDBKeyData primaryKeyData;
+ unsigned count;
+
+ IDBIterateCursorData isolatedCopy() const;
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBIterateCursorData&);
+};
+
+template<class Encoder>
+void IDBIterateCursorData::encode(Encoder& encoder) const
+{
+ encoder << keyData << primaryKeyData << static_cast<uint64_t>(count);
+}
+
+template<class Decoder>
+bool IDBIterateCursorData::decode(Decoder& decoder, IDBIterateCursorData& iteratorCursorData)
+{
+ if (!decoder.decode(iteratorCursorData.keyData))
+ return false;
+
+ if (!decoder.decode(iteratorCursorData.primaryKeyData))
+ return false;
+
+ uint64_t count;
+ if (!decoder.decode(count))
+ return false;
+
+ if (count > std::numeric_limits<unsigned>::max())
+ return false;
+
+ iteratorCursorData.count = static_cast<unsigned>(count);
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp
new file mode 100644
index 000000000..b0075fe37
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBObjectStoreInfo.h"
+#include <wtf/text/StringBuilder.h>
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBObjectStoreInfo::IDBObjectStoreInfo()
+{
+}
+
+IDBObjectStoreInfo::IDBObjectStoreInfo(uint64_t identifier, const String& name, std::optional<IDBKeyPath>&& keyPath, bool autoIncrement)
+ : m_identifier(identifier)
+ , m_name(name)
+ , m_keyPath(WTFMove(keyPath))
+ , m_autoIncrement(autoIncrement)
+{
+}
+
+IDBIndexInfo IDBObjectStoreInfo::createNewIndex(const String& name, IDBKeyPath&& keyPath, bool unique, bool multiEntry)
+{
+ IDBIndexInfo info(++m_maxIndexID, m_identifier, name, WTFMove(keyPath), unique, multiEntry);
+ m_indexMap.set(info.identifier(), info);
+ return info;
+}
+
+void IDBObjectStoreInfo::addExistingIndex(const IDBIndexInfo& info)
+{
+ ASSERT(!m_indexMap.contains(info.identifier()));
+
+ if (info.identifier() > m_maxIndexID)
+ m_maxIndexID = info.identifier();
+
+ m_indexMap.set(info.identifier(), info);
+}
+
+bool IDBObjectStoreInfo::hasIndex(const String& name) const
+{
+ for (auto& index : m_indexMap.values()) {
+ if (index.name() == name)
+ return true;
+ }
+
+ return false;
+}
+
+bool IDBObjectStoreInfo::hasIndex(uint64_t indexIdentifier) const
+{
+ return m_indexMap.contains(indexIdentifier);
+}
+
+IDBIndexInfo* IDBObjectStoreInfo::infoForExistingIndex(const String& name)
+{
+ for (auto& index : m_indexMap.values()) {
+ if (index.name() == name)
+ return &index;
+ }
+
+ return nullptr;
+}
+
+IDBIndexInfo* IDBObjectStoreInfo::infoForExistingIndex(uint64_t identifier)
+{
+ auto iterator = m_indexMap.find(identifier);
+ if (iterator == m_indexMap.end())
+ return nullptr;
+
+ return &iterator->value;
+}
+
+IDBObjectStoreInfo IDBObjectStoreInfo::isolatedCopy() const
+{
+ IDBObjectStoreInfo result = { m_identifier, m_name.isolatedCopy(), WebCore::isolatedCopy(m_keyPath), m_autoIncrement };
+
+ for (auto& iterator : m_indexMap) {
+ result.m_indexMap.set(iterator.key, iterator.value.isolatedCopy());
+ if (iterator.key > result.m_maxIndexID)
+ result.m_maxIndexID = iterator.key;
+ }
+
+ ASSERT(result.m_maxIndexID == m_maxIndexID);
+
+ return result;
+}
+
+Vector<String> IDBObjectStoreInfo::indexNames() const
+{
+ Vector<String> names;
+ names.reserveCapacity(m_indexMap.size());
+ for (auto& index : m_indexMap.values())
+ names.uncheckedAppend(index.name());
+
+ return names;
+}
+
+void IDBObjectStoreInfo::deleteIndex(const String& indexName)
+{
+ auto* info = infoForExistingIndex(indexName);
+ if (!info)
+ return;
+
+ m_indexMap.remove(info->identifier());
+}
+
+void IDBObjectStoreInfo::deleteIndex(uint64_t indexIdentifier)
+{
+ m_indexMap.remove(indexIdentifier);
+}
+
+#if !LOG_DISABLED
+String IDBObjectStoreInfo::loggingString(int indent) const
+{
+ StringBuilder builder;
+ for (int i = 0; i < indent; ++i)
+ builder.append(' ');
+
+ builder.appendLiteral("Object store: ");
+ builder.append(m_name);
+ builder.appendNumber(m_identifier);
+ for (auto index : m_indexMap.values()) {
+ builder.append(index.loggingString(indent + 1));
+ builder.append('\n');
+ }
+
+ return builder.toString();
+}
+
+String IDBObjectStoreInfo::condensedLoggingString() const
+{
+ return String::format("<OS: %s (%" PRIu64 ")>", m_name.utf8().data(), m_identifier);
+}
+
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h
new file mode 100644
index 000000000..0b8525909
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBIndexInfo.h"
+#include "IDBKeyPath.h"
+#include <wtf/HashMap.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class IDBObjectStoreInfo {
+public:
+ WEBCORE_EXPORT IDBObjectStoreInfo();
+ IDBObjectStoreInfo(uint64_t identifier, const String& name, std::optional<IDBKeyPath>&&, bool autoIncrement);
+
+ uint64_t identifier() const { return m_identifier; }
+ const String& name() const { return m_name; }
+ const std::optional<IDBKeyPath>& keyPath() const { return m_keyPath; }
+ bool autoIncrement() const { return m_autoIncrement; }
+ uint64_t maxIndexID() const { return m_maxIndexID; }
+
+ void rename(const String& newName) { m_name = newName; }
+
+ IDBObjectStoreInfo isolatedCopy() const;
+
+ IDBIndexInfo createNewIndex(const String& name, IDBKeyPath&&, bool unique, bool multiEntry);
+ void addExistingIndex(const IDBIndexInfo&);
+ bool hasIndex(const String& name) const;
+ bool hasIndex(uint64_t indexIdentifier) const;
+ IDBIndexInfo* infoForExistingIndex(const String& name);
+ IDBIndexInfo* infoForExistingIndex(uint64_t identifier);
+
+ Vector<String> indexNames() const;
+ const HashMap<uint64_t, IDBIndexInfo>& indexMap() const { return m_indexMap; }
+
+ void deleteIndex(const String& indexName);
+ void deleteIndex(uint64_t indexIdentifier);
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBObjectStoreInfo&);
+
+#if !LOG_DISABLED
+ String loggingString(int indent = 0) const;
+ String condensedLoggingString() const;
+#endif
+
+private:
+ uint64_t m_identifier { 0 };
+ String m_name;
+ std::optional<IDBKeyPath> m_keyPath;
+ bool m_autoIncrement { false };
+ uint64_t m_maxIndexID { 0 };
+
+ HashMap<uint64_t, IDBIndexInfo> m_indexMap;
+};
+
+template<class Encoder>
+void IDBObjectStoreInfo::encode(Encoder& encoder) const
+{
+ encoder << m_identifier << m_name << m_keyPath << m_autoIncrement << m_maxIndexID << m_indexMap;
+}
+
+template<class Decoder>
+bool IDBObjectStoreInfo::decode(Decoder& decoder, IDBObjectStoreInfo& info)
+{
+ if (!decoder.decode(info.m_identifier))
+ return false;
+
+ if (!decoder.decode(info.m_name))
+ return false;
+
+ if (!decoder.decode(info.m_keyPath))
+ return false;
+
+ if (!decoder.decode(info.m_autoIncrement))
+ return false;
+
+ if (!decoder.decode(info.m_maxIndexID))
+ return false;
+
+ if (!decoder.decode(info.m_indexMap))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp
new file mode 100644
index 000000000..574ea8b01
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBRequestData.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToServer.h"
+#include "IDBDatabase.h"
+#include "IDBOpenDBRequest.h"
+
+namespace WebCore {
+
+IDBRequestData::IDBRequestData()
+{
+}
+
+IDBRequestData::IDBRequestData(const IDBClient::IDBConnectionProxy& connectionProxy, const IDBOpenDBRequest& request)
+ : m_serverConnectionIdentifier(connectionProxy.serverConnectionIdentifier())
+ , m_requestIdentifier(std::make_unique<IDBResourceIdentifier>(connectionProxy, request))
+ , m_databaseIdentifier(request.databaseIdentifier())
+ , m_requestedVersion(request.version())
+ , m_requestType(request.requestType())
+{
+}
+
+IDBRequestData::IDBRequestData(IDBClient::TransactionOperation& operation)
+ : m_serverConnectionIdentifier(operation.transaction().database().connectionProxy().serverConnectionIdentifier())
+ , m_requestIdentifier(std::make_unique<IDBResourceIdentifier>(operation.identifier()))
+ , m_transactionIdentifier(std::make_unique<IDBResourceIdentifier>(operation.transactionIdentifier()))
+ , m_objectStoreIdentifier(operation.objectStoreIdentifier())
+ , m_indexIdentifier(operation.indexIdentifier())
+{
+ if (m_indexIdentifier)
+ m_indexRecordType = operation.indexRecordType();
+
+ if (operation.cursorIdentifier())
+ m_cursorIdentifier = std::make_unique<IDBResourceIdentifier>(*operation.cursorIdentifier());
+}
+
+IDBRequestData::IDBRequestData(const IDBRequestData& other)
+ : m_serverConnectionIdentifier(other.m_serverConnectionIdentifier)
+ , m_objectStoreIdentifier(other.m_objectStoreIdentifier)
+ , m_indexIdentifier(other.m_indexIdentifier)
+ , m_indexRecordType(other.m_indexRecordType)
+ , m_databaseIdentifier(other.m_databaseIdentifier)
+ , m_requestedVersion(other.m_requestedVersion)
+ , m_requestType(other.m_requestType)
+{
+ if (other.m_requestIdentifier)
+ m_requestIdentifier = std::make_unique<IDBResourceIdentifier>(*other.m_requestIdentifier);
+ if (other.m_transactionIdentifier)
+ m_transactionIdentifier = std::make_unique<IDBResourceIdentifier>(*other.m_transactionIdentifier);
+ if (other.m_cursorIdentifier)
+ m_cursorIdentifier = std::make_unique<IDBResourceIdentifier>(*other.m_cursorIdentifier);
+}
+
+IDBRequestData::IDBRequestData(const IDBRequestData& that, IsolatedCopyTag)
+{
+ isolatedCopy(that, *this);
+}
+
+
+IDBRequestData IDBRequestData::isolatedCopy() const
+{
+ return { *this, IsolatedCopy };
+}
+
+void IDBRequestData::isolatedCopy(const IDBRequestData& source, IDBRequestData& destination)
+{
+ destination.m_serverConnectionIdentifier = source.m_serverConnectionIdentifier;
+ destination.m_objectStoreIdentifier = source.m_objectStoreIdentifier;
+ destination.m_indexIdentifier = source.m_indexIdentifier;
+ destination.m_indexRecordType = source.m_indexRecordType;
+ destination.m_requestedVersion = source.m_requestedVersion;
+ destination.m_requestType = source.m_requestType;
+
+ destination.m_databaseIdentifier = source.m_databaseIdentifier.isolatedCopy();
+
+ if (source.m_requestIdentifier)
+ destination.m_requestIdentifier = std::make_unique<IDBResourceIdentifier>(*source.m_requestIdentifier);
+ if (source.m_transactionIdentifier)
+ destination.m_transactionIdentifier = std::make_unique<IDBResourceIdentifier>(*source.m_transactionIdentifier);
+ if (source.m_cursorIdentifier)
+ destination.m_cursorIdentifier = std::make_unique<IDBResourceIdentifier>(*source.m_cursorIdentifier);
+}
+
+uint64_t IDBRequestData::serverConnectionIdentifier() const
+{
+ ASSERT(m_serverConnectionIdentifier);
+ return m_serverConnectionIdentifier;
+}
+
+IDBResourceIdentifier IDBRequestData::requestIdentifier() const
+{
+ ASSERT(m_requestIdentifier);
+ return *m_requestIdentifier;
+}
+
+IDBResourceIdentifier IDBRequestData::transactionIdentifier() const
+{
+ ASSERT(m_transactionIdentifier);
+ return *m_transactionIdentifier;
+}
+
+IDBResourceIdentifier IDBRequestData::cursorIdentifier() const
+{
+ ASSERT(m_cursorIdentifier);
+ return *m_cursorIdentifier;
+}
+
+uint64_t IDBRequestData::objectStoreIdentifier() const
+{
+ ASSERT(m_objectStoreIdentifier);
+ return m_objectStoreIdentifier;
+}
+
+uint64_t IDBRequestData::indexIdentifier() const
+{
+ ASSERT(m_objectStoreIdentifier || m_indexIdentifier);
+ return m_indexIdentifier;
+}
+
+IndexedDB::IndexRecordType IDBRequestData::indexRecordType() const
+{
+ ASSERT(m_indexIdentifier);
+ return m_indexRecordType;
+}
+
+uint64_t IDBRequestData::requestedVersion() const
+{
+ return m_requestedVersion;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h
new file mode 100644
index 000000000..8948bb909
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBDatabaseIdentifier.h"
+#include "IDBResourceIdentifier.h"
+#include "IndexedDB.h"
+
+namespace WebCore {
+
+class IDBOpenDBRequest;
+class IDBTransaction;
+
+namespace IndexedDB {
+enum class IndexRecordType;
+}
+
+namespace IDBClient {
+class IDBConnectionProxy;
+class TransactionOperation;
+}
+
+class IDBRequestData {
+public:
+ IDBRequestData(const IDBClient::IDBConnectionProxy&, const IDBOpenDBRequest&);
+ explicit IDBRequestData(IDBClient::TransactionOperation&);
+ IDBRequestData(const IDBRequestData&);
+
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBRequestData(const IDBRequestData&, IsolatedCopyTag);
+ IDBRequestData isolatedCopy() const;
+
+ uint64_t serverConnectionIdentifier() const;
+ IDBResourceIdentifier requestIdentifier() const;
+ IDBResourceIdentifier transactionIdentifier() const;
+ uint64_t objectStoreIdentifier() const;
+ uint64_t indexIdentifier() const;
+ IndexedDB::IndexRecordType indexRecordType() const;
+ IDBResourceIdentifier cursorIdentifier() const;
+
+ const IDBDatabaseIdentifier& databaseIdentifier() const { return m_databaseIdentifier; }
+ uint64_t requestedVersion() const;
+
+ bool isOpenRequest() const { return m_requestType == IndexedDB::RequestType::Open; }
+ bool isDeleteRequest() const { return m_requestType == IndexedDB::RequestType::Delete; }
+
+ IDBRequestData isolatedCopy();
+
+ WEBCORE_EXPORT IDBRequestData();
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBRequestData&);
+
+private:
+ static void isolatedCopy(const IDBRequestData& source, IDBRequestData& destination);
+
+ uint64_t m_serverConnectionIdentifier { 0 };
+ std::unique_ptr<IDBResourceIdentifier> m_requestIdentifier;
+ std::unique_ptr<IDBResourceIdentifier> m_transactionIdentifier;
+ std::unique_ptr<IDBResourceIdentifier> m_cursorIdentifier;
+ uint64_t m_objectStoreIdentifier { 0 };
+ uint64_t m_indexIdentifier { 0 };
+ IndexedDB::IndexRecordType m_indexRecordType;
+
+ IDBDatabaseIdentifier m_databaseIdentifier;
+ uint64_t m_requestedVersion { 0 };
+
+ IndexedDB::RequestType m_requestType { IndexedDB::RequestType::Other };
+};
+
+template<class Encoder>
+void IDBRequestData::encode(Encoder& encoder) const
+{
+ encoder << m_serverConnectionIdentifier << m_objectStoreIdentifier << m_indexIdentifier << m_databaseIdentifier << m_requestedVersion;
+
+ encoder.encodeEnum(m_indexRecordType);
+ encoder.encodeEnum(m_requestType);
+
+ encoder << !!m_requestIdentifier;
+ if (m_requestIdentifier)
+ encoder << *m_requestIdentifier;
+
+ encoder << !!m_transactionIdentifier;
+ if (m_transactionIdentifier)
+ encoder << *m_transactionIdentifier;
+
+ encoder << !!m_cursorIdentifier;
+ if (m_cursorIdentifier)
+ encoder << *m_cursorIdentifier;
+}
+
+template<class Decoder>
+bool IDBRequestData::decode(Decoder& decoder, IDBRequestData& request)
+{
+ if (!decoder.decode(request.m_serverConnectionIdentifier))
+ return false;
+
+ if (!decoder.decode(request.m_objectStoreIdentifier))
+ return false;
+
+ if (!decoder.decode(request.m_indexIdentifier))
+ return false;
+
+ if (!decoder.decode(request.m_databaseIdentifier))
+ return false;
+
+ if (!decoder.decode(request.m_requestedVersion))
+ return false;
+
+ if (!decoder.decodeEnum(request.m_indexRecordType))
+ return false;
+
+ if (!decoder.decodeEnum(request.m_requestType))
+ return false;
+
+ bool hasObject;
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ std::unique_ptr<IDBResourceIdentifier> object = std::make_unique<IDBResourceIdentifier>();
+ if (!decoder.decode(*object))
+ return false;
+ request.m_requestIdentifier = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ std::unique_ptr<IDBResourceIdentifier> object = std::make_unique<IDBResourceIdentifier>();
+ if (!decoder.decode(*object))
+ return false;
+ request.m_transactionIdentifier = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ std::unique_ptr<IDBResourceIdentifier> object = std::make_unique<IDBResourceIdentifier>();
+ if (!decoder.decode(*object))
+ return false;
+ request.m_cursorIdentifier = WTFMove(object);
+ }
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp
new file mode 100644
index 000000000..d7e3f1742
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBResourceIdentifier.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToClient.h"
+#include "IDBConnectionToServer.h"
+#include "IDBRequest.h"
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+static uint64_t nextClientResourceNumber()
+{
+ static std::atomic<uint64_t> currentNumber(1);
+ return currentNumber += 2;
+}
+
+static uint64_t nextServerResourceNumber()
+{
+ ASSERT(isMainThread());
+ static uint64_t currentNumber = 0;
+ return currentNumber += 2;
+}
+
+IDBResourceIdentifier::IDBResourceIdentifier()
+{
+}
+
+IDBResourceIdentifier::IDBResourceIdentifier(uint64_t connectionIdentifier, uint64_t resourceIdentifier)
+ : m_idbConnectionIdentifier(connectionIdentifier)
+ , m_resourceNumber(resourceIdentifier)
+{
+}
+
+IDBResourceIdentifier::IDBResourceIdentifier(const IDBClient::IDBConnectionProxy& connectionProxy)
+ : m_idbConnectionIdentifier(connectionProxy.serverConnectionIdentifier())
+ , m_resourceNumber(nextClientResourceNumber())
+{
+}
+
+IDBResourceIdentifier::IDBResourceIdentifier(const IDBClient::IDBConnectionProxy& connectionProxy, const IDBRequest& request)
+ : m_idbConnectionIdentifier(connectionProxy.serverConnectionIdentifier())
+ , m_resourceNumber(request.resourceIdentifier().m_resourceNumber)
+{
+}
+
+IDBResourceIdentifier::IDBResourceIdentifier(const IDBServer::IDBConnectionToClient& connection)
+ : m_idbConnectionIdentifier(connection.identifier())
+ , m_resourceNumber(nextServerResourceNumber())
+{
+}
+
+IDBResourceIdentifier IDBResourceIdentifier::isolatedCopy() const
+{
+ return IDBResourceIdentifier(m_idbConnectionIdentifier, m_resourceNumber);
+}
+
+IDBResourceIdentifier IDBResourceIdentifier::emptyValue()
+{
+ return IDBResourceIdentifier(0, 0);
+}
+
+IDBResourceIdentifier IDBResourceIdentifier::deletedValue()
+{
+ return IDBResourceIdentifier(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max());
+}
+
+bool IDBResourceIdentifier::isHashTableDeletedValue() const
+{
+ return m_idbConnectionIdentifier == std::numeric_limits<uint64_t>::max()
+ && m_resourceNumber == std::numeric_limits<uint64_t>::max();
+}
+
+#if !LOG_DISABLED
+String IDBResourceIdentifier::loggingString() const
+{
+ return String::format("<%" PRIu64", %" PRIu64">", m_idbConnectionIdentifier, m_resourceNumber);
+}
+#endif
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h
new file mode 100644
index 000000000..91d7810cc
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015, 2016 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class IDBRequest;
+
+namespace IDBClient {
+class IDBConnectionProxy;
+}
+
+namespace IDBServer {
+class IDBConnectionToClient;
+}
+
+class IDBResourceIdentifier {
+public:
+ explicit IDBResourceIdentifier(const IDBClient::IDBConnectionProxy&);
+ IDBResourceIdentifier(const IDBClient::IDBConnectionProxy&, const IDBRequest&);
+ explicit IDBResourceIdentifier(const IDBServer::IDBConnectionToClient&);
+
+ static IDBResourceIdentifier deletedValue();
+ WEBCORE_EXPORT bool isHashTableDeletedValue() const;
+
+ static IDBResourceIdentifier emptyValue();
+ bool isEmpty() const
+ {
+ return !m_resourceNumber && !m_idbConnectionIdentifier;
+ }
+
+ unsigned hash() const
+ {
+ uint64_t hashCodes[2] = { m_idbConnectionIdentifier, m_resourceNumber };
+ return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes);
+ }
+
+ bool operator==(const IDBResourceIdentifier& other) const
+ {
+ return m_idbConnectionIdentifier == other.m_idbConnectionIdentifier
+ && m_resourceNumber == other.m_resourceNumber;
+ }
+
+ uint64_t connectionIdentifier() const { return m_idbConnectionIdentifier; }
+
+ IDBResourceIdentifier isolatedCopy() const;
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+ WEBCORE_EXPORT IDBResourceIdentifier();
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBResourceIdentifier&);
+
+private:
+ IDBResourceIdentifier(uint64_t connectionIdentifier, uint64_t resourceIdentifier);
+ uint64_t m_idbConnectionIdentifier { 0 };
+ uint64_t m_resourceNumber { 0 };
+};
+
+struct IDBResourceIdentifierHash {
+ static unsigned hash(const IDBResourceIdentifier& a) { return a.hash(); }
+ static bool equal(const IDBResourceIdentifier& a, const IDBResourceIdentifier& b) { return a == b; }
+ static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+struct IDBResourceIdentifierHashTraits : WTF::CustomHashTraits<IDBResourceIdentifier> {
+ static const bool hasIsEmptyValueFunction = true;
+ static const bool emptyValueIsZero = false;
+
+ static IDBResourceIdentifier emptyValue()
+ {
+ return IDBResourceIdentifier::emptyValue();
+ }
+
+ static bool isEmptyValue(const IDBResourceIdentifier& identifier)
+ {
+ return identifier.isEmpty();
+ }
+
+ static void constructDeletedValue(IDBResourceIdentifier& identifier)
+ {
+ identifier = IDBResourceIdentifier::deletedValue();
+ }
+
+ static bool isDeletedValue(const IDBResourceIdentifier& identifier)
+ {
+ return identifier.isHashTableDeletedValue();
+ }
+};
+
+template<class Encoder>
+void IDBResourceIdentifier::encode(Encoder& encoder) const
+{
+ encoder << m_idbConnectionIdentifier << m_resourceNumber;
+}
+
+template<class Decoder>
+bool IDBResourceIdentifier::decode(Decoder& decoder, IDBResourceIdentifier& identifier)
+{
+ if (!decoder.decode(identifier.m_idbConnectionIdentifier))
+ return false;
+
+ if (!decoder.decode(identifier.m_resourceNumber))
+ return false;
+
+ return true;
+}
+
+} // namespace WebCore
+
+namespace WTF {
+
+template<> struct HashTraits<WebCore::IDBResourceIdentifier> : WebCore::IDBResourceIdentifierHashTraits { };
+template<> struct DefaultHash<WebCore::IDBResourceIdentifier> {
+ typedef WebCore::IDBResourceIdentifierHash Hash;
+};
+
+} // namespace WTF
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp
new file mode 100644
index 000000000..6c6ae6ba0
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBResultData.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "UniqueIDBDatabase.h"
+#include "UniqueIDBDatabaseConnection.h"
+#include "UniqueIDBDatabaseTransaction.h"
+
+namespace WebCore {
+
+IDBResultData::IDBResultData()
+{
+}
+
+IDBResultData::IDBResultData(const IDBResourceIdentifier& requestIdentifier)
+ : m_requestIdentifier(requestIdentifier)
+{
+}
+
+IDBResultData::IDBResultData(IDBResultType type, const IDBResourceIdentifier& requestIdentifier)
+ : m_type(type)
+ , m_requestIdentifier(requestIdentifier)
+{
+}
+
+IDBResultData::IDBResultData(const IDBResultData& other)
+ : m_type(other.m_type)
+ , m_requestIdentifier(other.m_requestIdentifier)
+ , m_error(other.m_error)
+ , m_databaseConnectionIdentifier(other.m_databaseConnectionIdentifier)
+ , m_resultInteger(other.m_resultInteger)
+{
+ if (other.m_databaseInfo)
+ m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*other.m_databaseInfo);
+ if (other.m_transactionInfo)
+ m_transactionInfo = std::make_unique<IDBTransactionInfo>(*other.m_transactionInfo);
+ if (other.m_resultKey)
+ m_resultKey = std::make_unique<IDBKeyData>(*other.m_resultKey);
+ if (other.m_getResult)
+ m_getResult = std::make_unique<IDBGetResult>(*other.m_getResult);
+ if (other.m_getAllResult)
+ m_getAllResult = std::make_unique<IDBGetAllResult>(*other.m_getAllResult);
+}
+
+IDBResultData::IDBResultData(const IDBResultData& that, IsolatedCopyTag)
+{
+ isolatedCopy(that, *this);
+}
+
+IDBResultData IDBResultData::isolatedCopy() const
+{
+ return { *this, IsolatedCopy };
+}
+
+void IDBResultData::isolatedCopy(const IDBResultData& source, IDBResultData& destination)
+{
+ destination.m_type = source.m_type;
+ destination.m_requestIdentifier = source.m_requestIdentifier.isolatedCopy();
+ destination.m_error = source.m_error.isolatedCopy();
+ destination.m_databaseConnectionIdentifier = source.m_databaseConnectionIdentifier;
+ destination.m_resultInteger = source.m_resultInteger;
+
+ if (source.m_databaseInfo)
+ destination.m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*source.m_databaseInfo, IDBDatabaseInfo::IsolatedCopy);
+ if (source.m_transactionInfo)
+ destination.m_transactionInfo = std::make_unique<IDBTransactionInfo>(*source.m_transactionInfo, IDBTransactionInfo::IsolatedCopy);
+ if (source.m_resultKey)
+ destination.m_resultKey = std::make_unique<IDBKeyData>(*source.m_resultKey, IDBKeyData::IsolatedCopy);
+ if (source.m_getResult)
+ destination.m_getResult = std::make_unique<IDBGetResult>(*source.m_getResult, IDBGetResult::IsolatedCopy);
+ if (source.m_getAllResult)
+ destination.m_getAllResult = std::make_unique<IDBGetAllResult>(*source.m_getAllResult, IDBGetAllResult::IsolatedCopy);
+}
+
+IDBResultData IDBResultData::error(const IDBResourceIdentifier& requestIdentifier, const IDBError& error)
+{
+ IDBResultData result { requestIdentifier };
+ result.m_type = IDBResultType::Error;
+ result.m_error = error;
+ return result;
+}
+
+IDBResultData IDBResultData::openDatabaseSuccess(const IDBResourceIdentifier& requestIdentifier, IDBServer::UniqueIDBDatabaseConnection& connection)
+{
+ IDBResultData result { requestIdentifier };
+ result.m_type = IDBResultType::OpenDatabaseSuccess;
+ result.m_databaseConnectionIdentifier = connection.identifier();
+ result.m_databaseInfo = std::make_unique<IDBDatabaseInfo>(connection.database().info());
+ return result;
+}
+
+
+IDBResultData IDBResultData::openDatabaseUpgradeNeeded(const IDBResourceIdentifier& requestIdentifier, IDBServer::UniqueIDBDatabaseTransaction& transaction)
+{
+ IDBResultData result { requestIdentifier };
+ result.m_type = IDBResultType::OpenDatabaseUpgradeNeeded;
+ result.m_databaseConnectionIdentifier = transaction.databaseConnection().identifier();
+ result.m_databaseInfo = std::make_unique<IDBDatabaseInfo>(transaction.databaseConnection().database().info());
+ result.m_transactionInfo = std::make_unique<IDBTransactionInfo>(transaction.info());
+ return result;
+}
+
+IDBResultData IDBResultData::deleteDatabaseSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBDatabaseInfo& info)
+{
+ IDBResultData result {IDBResultType::DeleteDatabaseSuccess, requestIdentifier };
+ result.m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
+ return result;
+}
+
+IDBResultData IDBResultData::createObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::CreateObjectStoreSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::deleteObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::DeleteObjectStoreSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::renameObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::RenameObjectStoreSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::clearObjectStoreSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::ClearObjectStoreSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::createIndexSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::CreateIndexSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::deleteIndexSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::DeleteIndexSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::renameIndexSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::RenameIndexSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::putOrAddSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBKeyData& resultKey)
+{
+ IDBResultData result { IDBResultType::PutOrAddSuccess, requestIdentifier };
+ result.m_resultKey = std::make_unique<IDBKeyData>(resultKey);
+ return result;
+}
+
+IDBResultData IDBResultData::getRecordSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult)
+{
+ IDBResultData result { IDBResultType::GetRecordSuccess, requestIdentifier };
+ result.m_getResult = std::make_unique<IDBGetResult>(getResult);
+ return result;
+}
+
+IDBResultData IDBResultData::getAllRecordsSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetAllResult& getAllResult)
+{
+ IDBResultData result { IDBResultType::GetAllRecordsSuccess, requestIdentifier };
+ result.m_getAllResult = std::make_unique<IDBGetAllResult>(getAllResult);
+ return result;
+}
+
+IDBResultData IDBResultData::getCountSuccess(const IDBResourceIdentifier& requestIdentifier, uint64_t count)
+{
+ IDBResultData result { IDBResultType::GetRecordSuccess, requestIdentifier };
+ result.m_resultInteger = count;
+ return result;
+}
+
+IDBResultData IDBResultData::deleteRecordSuccess(const IDBResourceIdentifier& requestIdentifier)
+{
+ return { IDBResultType::DeleteRecordSuccess, requestIdentifier };
+}
+
+IDBResultData IDBResultData::openCursorSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult)
+{
+ IDBResultData result { IDBResultType::OpenCursorSuccess, requestIdentifier };
+ result.m_getResult = std::make_unique<IDBGetResult>(getResult);
+ return result;
+}
+
+IDBResultData IDBResultData::iterateCursorSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult)
+{
+ IDBResultData result { IDBResultType::IterateCursorSuccess, requestIdentifier };
+ result.m_getResult = std::make_unique<IDBGetResult>(getResult);
+ return result;
+}
+
+const IDBDatabaseInfo& IDBResultData::databaseInfo() const
+{
+ RELEASE_ASSERT(m_databaseInfo);
+ return *m_databaseInfo;
+}
+
+const IDBTransactionInfo& IDBResultData::transactionInfo() const
+{
+ RELEASE_ASSERT(m_transactionInfo);
+ return *m_transactionInfo;
+}
+
+const IDBGetResult& IDBResultData::getResult() const
+{
+ RELEASE_ASSERT(m_getResult);
+ return *m_getResult;
+}
+
+const IDBGetAllResult& IDBResultData::getAllResult() const
+{
+ RELEASE_ASSERT(m_getAllResult);
+ return *m_getAllResult;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBResultData.h b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.h
new file mode 100644
index 000000000..217c9a3ca
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBResultData.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBDatabaseInfo.h"
+#include "IDBError.h"
+#include "IDBGetAllResult.h"
+#include "IDBGetResult.h"
+#include "IDBKeyData.h"
+#include "IDBResourceIdentifier.h"
+#include "IDBTransactionInfo.h"
+#include "ThreadSafeDataBuffer.h"
+
+namespace WebCore {
+
+class ThreadSafeDataBuffer;
+
+enum class IDBResultType {
+ Error,
+ OpenDatabaseSuccess,
+ OpenDatabaseUpgradeNeeded,
+ DeleteDatabaseSuccess,
+ CreateObjectStoreSuccess,
+ DeleteObjectStoreSuccess,
+ ClearObjectStoreSuccess,
+ PutOrAddSuccess,
+ GetRecordSuccess,
+ GetAllRecordsSuccess,
+ GetCountSuccess,
+ DeleteRecordSuccess,
+ CreateIndexSuccess,
+ DeleteIndexSuccess,
+ OpenCursorSuccess,
+ IterateCursorSuccess,
+ RenameObjectStoreSuccess,
+ RenameIndexSuccess,
+};
+
+namespace IDBServer {
+class UniqueIDBDatabaseConnection;
+class UniqueIDBDatabaseTransaction;
+}
+
+class IDBResultData {
+public:
+ static IDBResultData error(const IDBResourceIdentifier&, const IDBError&);
+ static IDBResultData openDatabaseSuccess(const IDBResourceIdentifier&, IDBServer::UniqueIDBDatabaseConnection&);
+ static IDBResultData openDatabaseUpgradeNeeded(const IDBResourceIdentifier&, IDBServer::UniqueIDBDatabaseTransaction&);
+ static IDBResultData deleteDatabaseSuccess(const IDBResourceIdentifier&, const IDBDatabaseInfo&);
+ static IDBResultData createObjectStoreSuccess(const IDBResourceIdentifier&);
+ static IDBResultData deleteObjectStoreSuccess(const IDBResourceIdentifier&);
+ static IDBResultData renameObjectStoreSuccess(const IDBResourceIdentifier&);
+ static IDBResultData clearObjectStoreSuccess(const IDBResourceIdentifier&);
+ static IDBResultData createIndexSuccess(const IDBResourceIdentifier&);
+ static IDBResultData deleteIndexSuccess(const IDBResourceIdentifier&);
+ static IDBResultData renameIndexSuccess(const IDBResourceIdentifier&);
+ static IDBResultData putOrAddSuccess(const IDBResourceIdentifier&, const IDBKeyData&);
+ static IDBResultData getRecordSuccess(const IDBResourceIdentifier&, const IDBGetResult&);
+ static IDBResultData getAllRecordsSuccess(const IDBResourceIdentifier&, const IDBGetAllResult&);
+ static IDBResultData getCountSuccess(const IDBResourceIdentifier&, uint64_t count);
+ static IDBResultData deleteRecordSuccess(const IDBResourceIdentifier&);
+ static IDBResultData openCursorSuccess(const IDBResourceIdentifier&, const IDBGetResult&);
+ static IDBResultData iterateCursorSuccess(const IDBResourceIdentifier&, const IDBGetResult&);
+
+ WEBCORE_EXPORT IDBResultData(const IDBResultData&);
+
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBResultData(const IDBResultData&, IsolatedCopyTag);
+ IDBResultData isolatedCopy() const;
+
+ IDBResultType type() const { return m_type; }
+ IDBResourceIdentifier requestIdentifier() const { return m_requestIdentifier; }
+
+ const IDBError& error() const { return m_error; }
+ uint64_t databaseConnectionIdentifier() const { return m_databaseConnectionIdentifier; }
+
+ const IDBDatabaseInfo& databaseInfo() const;
+ const IDBTransactionInfo& transactionInfo() const;
+
+ const IDBKeyData* resultKey() const { return m_resultKey.get(); }
+ uint64_t resultInteger() const { return m_resultInteger; }
+
+ WEBCORE_EXPORT const IDBGetResult& getResult() const;
+ WEBCORE_EXPORT const IDBGetAllResult& getAllResult() const;
+
+ WEBCORE_EXPORT IDBResultData();
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBResultData&);
+
+private:
+ IDBResultData(const IDBResourceIdentifier&);
+ IDBResultData(IDBResultType, const IDBResourceIdentifier&);
+
+ static void isolatedCopy(const IDBResultData& source, IDBResultData& destination);
+
+ IDBResultType m_type { IDBResultType::Error };
+ IDBResourceIdentifier m_requestIdentifier;
+
+ IDBError m_error;
+ uint64_t m_databaseConnectionIdentifier { 0 };
+ std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
+ std::unique_ptr<IDBTransactionInfo> m_transactionInfo;
+ std::unique_ptr<IDBKeyData> m_resultKey;
+ std::unique_ptr<IDBGetResult> m_getResult;
+ std::unique_ptr<IDBGetAllResult> m_getAllResult;
+ uint64_t m_resultInteger { 0 };
+};
+
+template<class Encoder>
+void IDBResultData::encode(Encoder& encoder) const
+{
+ encoder << m_requestIdentifier << m_error << m_databaseConnectionIdentifier << m_resultInteger;
+
+ encoder.encodeEnum(m_type);
+
+ encoder << !!m_databaseInfo;
+ if (m_databaseInfo)
+ encoder << *m_databaseInfo;
+
+ encoder << !!m_transactionInfo;
+ if (m_transactionInfo)
+ encoder << *m_transactionInfo;
+
+ encoder << !!m_resultKey;
+ if (m_resultKey)
+ encoder << *m_resultKey;
+
+ encoder << !!m_getResult;
+ if (m_getResult)
+ encoder << *m_getResult;
+
+ encoder << !!m_getAllResult;
+ if (m_getAllResult)
+ encoder << *m_getAllResult;
+}
+
+template<class Decoder> bool IDBResultData::decode(Decoder& decoder, IDBResultData& result)
+{
+ if (!decoder.decode(result.m_requestIdentifier))
+ return false;
+
+ if (!decoder.decode(result.m_error))
+ return false;
+
+ if (!decoder.decode(result.m_databaseConnectionIdentifier))
+ return false;
+
+ if (!decoder.decode(result.m_resultInteger))
+ return false;
+
+ if (!decoder.decodeEnum(result.m_type))
+ return false;
+
+ bool hasObject;
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBDatabaseInfo>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_databaseInfo = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBTransactionInfo>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_transactionInfo = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBKeyData>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_resultKey = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBGetResult>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_getResult = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBGetAllResult>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_getAllResult = WTFMove(object);
+ }
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp
new file mode 100644
index 000000000..0010c2443
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "IDBTransactionInfo.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBTransaction.h"
+
+namespace WebCore {
+
+IDBTransactionInfo::IDBTransactionInfo()
+{
+}
+
+IDBTransactionInfo::IDBTransactionInfo(const IDBResourceIdentifier& identifier)
+ : m_identifier(identifier)
+{
+}
+
+IDBTransactionInfo IDBTransactionInfo::clientTransaction(const IDBClient::IDBConnectionProxy& connectionProxy, const Vector<String>& objectStores, IDBTransactionMode mode)
+{
+ IDBTransactionInfo result((IDBResourceIdentifier(connectionProxy)));
+ result.m_mode = mode;
+ result.m_objectStores = objectStores;
+
+ return result;
+}
+
+IDBTransactionInfo IDBTransactionInfo::versionChange(const IDBServer::IDBConnectionToClient& connection, const IDBDatabaseInfo& originalDatabaseInfo, uint64_t newVersion)
+{
+ IDBTransactionInfo result((IDBResourceIdentifier(connection)));
+ result.m_mode = IDBTransactionMode::Versionchange;
+ result.m_newVersion = newVersion;
+ result.m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(originalDatabaseInfo);
+
+ return result;
+}
+
+IDBTransactionInfo::IDBTransactionInfo(const IDBTransactionInfo& info)
+ : m_identifier(info.identifier())
+ , m_mode(info.m_mode)
+ , m_newVersion(info.m_newVersion)
+ , m_objectStores(info.m_objectStores)
+{
+ if (info.m_originalDatabaseInfo)
+ m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(*info.m_originalDatabaseInfo);
+}
+
+IDBTransactionInfo::IDBTransactionInfo(const IDBTransactionInfo& that, IsolatedCopyTag)
+{
+ isolatedCopy(that, *this);
+}
+
+IDBTransactionInfo IDBTransactionInfo::isolatedCopy() const
+{
+ return { *this, IsolatedCopy };
+}
+
+void IDBTransactionInfo::isolatedCopy(const IDBTransactionInfo& source, IDBTransactionInfo& destination)
+{
+ destination.m_identifier = source.m_identifier.isolatedCopy();
+ destination.m_mode = source.m_mode;
+ destination.m_newVersion = source.m_newVersion;
+
+ destination.m_objectStores.reserveCapacity(source.m_objectStores.size());
+ for (auto& objectStore : source.m_objectStores)
+ destination.m_objectStores.uncheckedAppend(objectStore.isolatedCopy());
+
+ if (source.m_originalDatabaseInfo)
+ destination.m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(*source.m_originalDatabaseInfo, IDBDatabaseInfo::IsolatedCopy);
+}
+
+#if !LOG_DISABLED
+String IDBTransactionInfo::loggingString() const
+{
+ String modeString;
+ switch (m_mode) {
+ case IDBTransactionMode::Readonly:
+ modeString = ASCIILiteral("readonly");
+ break;
+ case IDBTransactionMode::Readwrite:
+ modeString = ASCIILiteral("readwrite");
+ break;
+ case IDBTransactionMode::Versionchange:
+ modeString = ASCIILiteral("versionchange");
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ return makeString("Transaction: ", m_identifier.loggingString(), " mode ", modeString, " newVersion ", String::number(m_newVersion));
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h
new file mode 100644
index 000000000..636286c86
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBDatabaseInfo.h"
+#include "IDBResourceIdentifier.h"
+#include "IDBTransactionMode.h"
+#include "IndexedDB.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+namespace IDBClient {
+class IDBConnectionProxy;
+}
+
+namespace IDBServer {
+class IDBConnectionToClient;
+}
+
+class IDBTransactionInfo {
+public:
+ static IDBTransactionInfo clientTransaction(const IDBClient::IDBConnectionProxy&, const Vector<String>& objectStores, IDBTransactionMode);
+ static IDBTransactionInfo versionChange(const IDBServer::IDBConnectionToClient&, const IDBDatabaseInfo& originalDatabaseInfo, uint64_t newVersion);
+
+ IDBTransactionInfo(const IDBTransactionInfo&);
+
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBTransactionInfo(const IDBTransactionInfo&, IsolatedCopyTag);
+
+ IDBTransactionInfo isolatedCopy() const;
+
+ const IDBResourceIdentifier& identifier() const { return m_identifier; }
+
+ IDBTransactionMode mode() const { return m_mode; }
+ uint64_t newVersion() const { return m_newVersion; }
+
+ const Vector<String>& objectStores() const { return m_objectStores; }
+
+ IDBDatabaseInfo* originalDatabaseInfo() const { return m_originalDatabaseInfo.get(); }
+
+ WEBCORE_EXPORT IDBTransactionInfo();
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBTransactionInfo&);
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+private:
+ IDBTransactionInfo(const IDBResourceIdentifier&);
+
+ static void isolatedCopy(const IDBTransactionInfo& source, IDBTransactionInfo& destination);
+
+ IDBResourceIdentifier m_identifier;
+
+ IDBTransactionMode m_mode { IDBTransactionMode::Readonly };
+ uint64_t m_newVersion { 0 };
+ Vector<String> m_objectStores;
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
+};
+
+template<class Encoder>
+void IDBTransactionInfo::encode(Encoder& encoder) const
+{
+ encoder << m_identifier << m_newVersion << m_objectStores;
+ encoder.encodeEnum(m_mode);
+
+ encoder << !!m_originalDatabaseInfo;
+ if (m_originalDatabaseInfo)
+ encoder << *m_originalDatabaseInfo;
+}
+
+template<class Decoder>
+bool IDBTransactionInfo::decode(Decoder& decoder, IDBTransactionInfo& info)
+{
+ if (!decoder.decode(info.m_identifier))
+ return false;
+
+ if (!decoder.decode(info.m_newVersion))
+ return false;
+
+ if (!decoder.decode(info.m_objectStores))
+ return false;
+
+ if (!decoder.decodeEnum(info.m_mode))
+ return false;
+
+ bool hasObject;
+ if (!decoder.decode(hasObject))
+ return false;
+
+ if (hasObject) {
+ std::unique_ptr<IDBDatabaseInfo> object = std::make_unique<IDBDatabaseInfo>();
+ if (!decoder.decode(*object))
+ return false;
+ info.m_originalDatabaseInfo = WTFMove(object);
+ }
+
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp
new file mode 100644
index 000000000..addd1f8c0
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "InProcessIDBServer.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "FileSystem.h"
+#include "IDBConnectionToClient.h"
+#include "IDBConnectionToServer.h"
+#include "IDBCursorInfo.h"
+#include "IDBGetRecordData.h"
+#include "IDBIterateCursorData.h"
+#include "IDBKeyRangeData.h"
+#include "IDBOpenDBRequest.h"
+#include "IDBRequestData.h"
+#include "IDBResultData.h"
+#include "IDBValue.h"
+#include "Logging.h"
+#include <wtf/RunLoop.h>
+
+namespace WebCore {
+
+Ref<InProcessIDBServer> InProcessIDBServer::create()
+{
+ Ref<InProcessIDBServer> server = adoptRef(*new InProcessIDBServer);
+ server->m_server->registerConnection(server->connectionToClient());
+ return server;
+}
+
+Ref<InProcessIDBServer> InProcessIDBServer::create(const String& databaseDirectoryPath)
+{
+ Ref<InProcessIDBServer> server = adoptRef(*new InProcessIDBServer(databaseDirectoryPath));
+ server->m_server->registerConnection(server->connectionToClient());
+ return server;
+}
+
+InProcessIDBServer::InProcessIDBServer()
+ : m_server(IDBServer::IDBServer::create(*this))
+{
+ relaxAdoptionRequirement();
+ m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this);
+ m_connectionToClient = IDBServer::IDBConnectionToClient::create(*this);
+}
+
+InProcessIDBServer::InProcessIDBServer(const String& databaseDirectoryPath)
+ : m_server(IDBServer::IDBServer::create(databaseDirectoryPath, *this))
+{
+ relaxAdoptionRequirement();
+ m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this);
+ m_connectionToClient = IDBServer::IDBConnectionToClient::create(*this);
+}
+
+uint64_t InProcessIDBServer::identifier() const
+{
+ // An instance of InProcessIDBServer always has a 1:1 relationship with its instance of IDBServer.
+ // Therefore the connection identifier between the two can always be "1".
+ return 1;
+}
+
+IDBClient::IDBConnectionToServer& InProcessIDBServer::connectionToServer() const
+{
+ return *m_connectionToServer;
+}
+
+IDBServer::IDBConnectionToClient& InProcessIDBServer::connectionToClient() const
+{
+ return *m_connectionToClient;
+}
+
+void InProcessIDBServer::deleteDatabase(const IDBRequestData& requestData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData] {
+ m_server->deleteDatabase(requestData);
+ });
+}
+
+void InProcessIDBServer::didDeleteDatabase(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didDeleteDatabase(resultData);
+ });
+}
+
+void InProcessIDBServer::openDatabase(const IDBRequestData& requestData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData] {
+ m_server->openDatabase(requestData);
+ });
+}
+
+void InProcessIDBServer::didOpenDatabase(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didOpenDatabase(resultData);
+ });
+}
+
+void InProcessIDBServer::didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), transactionIdentifier, error] {
+ m_connectionToServer->didAbortTransaction(transactionIdentifier, error);
+ });
+}
+
+void InProcessIDBServer::didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), transactionIdentifier, error] {
+ m_connectionToServer->didCommitTransaction(transactionIdentifier, error);
+ });
+}
+
+void InProcessIDBServer::didCreateObjectStore(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didCreateObjectStore(resultData);
+ });
+}
+
+void InProcessIDBServer::didDeleteObjectStore(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didDeleteObjectStore(resultData);
+ });
+}
+
+void InProcessIDBServer::didRenameObjectStore(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didRenameObjectStore(resultData);
+ });
+}
+
+void InProcessIDBServer::didClearObjectStore(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didClearObjectStore(resultData);
+ });
+}
+
+void InProcessIDBServer::didCreateIndex(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didCreateIndex(resultData);
+ });
+}
+
+void InProcessIDBServer::didDeleteIndex(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didDeleteIndex(resultData);
+ });
+}
+
+void InProcessIDBServer::didRenameIndex(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didRenameIndex(resultData);
+ });
+}
+
+void InProcessIDBServer::didPutOrAdd(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didPutOrAdd(resultData);
+ });
+}
+
+void InProcessIDBServer::didGetRecord(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didGetRecord(resultData);
+ });
+}
+
+void InProcessIDBServer::didGetAllRecords(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didGetAllRecords(resultData);
+ });
+}
+
+void InProcessIDBServer::didGetCount(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didGetCount(resultData);
+ });
+}
+
+void InProcessIDBServer::didDeleteRecord(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didDeleteRecord(resultData);
+ });
+}
+
+void InProcessIDBServer::didOpenCursor(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didOpenCursor(resultData);
+ });
+}
+
+void InProcessIDBServer::didIterateCursor(const IDBResultData& resultData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData] {
+ m_connectionToServer->didIterateCursor(resultData);
+ });
+}
+
+void InProcessIDBServer::abortTransaction(const IDBResourceIdentifier& resourceIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resourceIdentifier] {
+ m_server->abortTransaction(resourceIdentifier);
+ });
+}
+
+void InProcessIDBServer::commitTransaction(const IDBResourceIdentifier& resourceIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resourceIdentifier] {
+ m_server->commitTransaction(resourceIdentifier);
+ });
+}
+
+void InProcessIDBServer::didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, transactionIdentifier] {
+ m_server->didFinishHandlingVersionChangeTransaction(databaseConnectionIdentifier, transactionIdentifier);
+ });
+}
+
+void InProcessIDBServer::createObjectStore(const IDBRequestData& resultData, const IDBObjectStoreInfo& info)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), resultData, info] {
+ m_server->createObjectStore(resultData, info);
+ });
+}
+
+void InProcessIDBServer::deleteObjectStore(const IDBRequestData& requestData, const String& objectStoreName)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreName] {
+ m_server->deleteObjectStore(requestData, objectStoreName);
+ });
+}
+
+void InProcessIDBServer::renameObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& newName)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier, newName] {
+ m_server->renameObjectStore(requestData, objectStoreIdentifier, newName);
+ });
+}
+
+void InProcessIDBServer::clearObjectStore(const IDBRequestData& requestData, uint64_t objectStoreIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier] {
+ m_server->clearObjectStore(requestData, objectStoreIdentifier);
+ });
+}
+
+void InProcessIDBServer::createIndex(const IDBRequestData& requestData, const IDBIndexInfo& info)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, info] {
+ m_server->createIndex(requestData, info);
+ });
+}
+
+void InProcessIDBServer::deleteIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, const String& indexName)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier, indexName] {
+ m_server->deleteIndex(requestData, objectStoreIdentifier, indexName);
+ });
+}
+
+void InProcessIDBServer::renameIndex(const IDBRequestData& requestData, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, objectStoreIdentifier, indexIdentifier, newName] {
+ m_server->renameIndex(requestData, objectStoreIdentifier, indexIdentifier, newName);
+ });
+}
+
+void InProcessIDBServer::putOrAdd(const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, const IndexedDB::ObjectStoreOverwriteMode overwriteMode)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, keyData, value, overwriteMode] {
+ m_server->putOrAdd(requestData, keyData, value, overwriteMode);
+ });
+}
+
+void InProcessIDBServer::getRecord(const IDBRequestData& requestData, const IDBGetRecordData& getRecordData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, getRecordData] {
+ m_server->getRecord(requestData, getRecordData);
+ });
+}
+
+void InProcessIDBServer::getAllRecords(const IDBRequestData& requestData, const IDBGetAllRecordsData& getAllRecordsData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, getAllRecordsData] {
+ m_server->getAllRecords(requestData, getAllRecordsData);
+ });
+}
+
+void InProcessIDBServer::getCount(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, keyRangeData] {
+ m_server->getCount(requestData, keyRangeData);
+ });
+}
+
+void InProcessIDBServer::deleteRecord(const IDBRequestData& requestData, const IDBKeyRangeData& keyRangeData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, keyRangeData] {
+ m_server->deleteRecord(requestData, keyRangeData);
+ });
+}
+
+void InProcessIDBServer::openCursor(const IDBRequestData& requestData, const IDBCursorInfo& info)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, info] {
+ m_server->openCursor(requestData, info);
+ });
+}
+
+void InProcessIDBServer::iterateCursor(const IDBRequestData& requestData, const IDBIterateCursorData& data)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData, data] {
+ m_server->iterateCursor(requestData, data);
+ });
+}
+
+void InProcessIDBServer::establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo& info)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, info] {
+ m_server->establishTransaction(databaseConnectionIdentifier, info);
+ });
+}
+
+void InProcessIDBServer::fireVersionChangeEvent(IDBServer::UniqueIDBDatabaseConnection& connection, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier = connection.identifier(), requestIdentifier, requestedVersion] {
+ m_connectionToServer->fireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier, requestedVersion);
+ });
+}
+
+void InProcessIDBServer::didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError& error)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), transactionIdentifier, error] {
+ m_connectionToServer->didStartTransaction(transactionIdentifier, error);
+ });
+}
+
+void InProcessIDBServer::didCloseFromServer(IDBServer::UniqueIDBDatabaseConnection& connection, const IDBError& error)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier = connection.identifier(), error] {
+ m_connectionToServer->didCloseFromServer(databaseConnectionIdentifier, error);
+ });
+}
+
+void InProcessIDBServer::notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestIdentifier, oldVersion, newVersion] {
+ m_connectionToServer->notifyOpenDBRequestBlocked(requestIdentifier, oldVersion, newVersion);
+ });
+}
+
+void InProcessIDBServer::databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier] {
+ m_server->databaseConnectionPendingClose(databaseConnectionIdentifier);
+ });
+}
+
+void InProcessIDBServer::databaseConnectionClosed(uint64_t databaseConnectionIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier] {
+ m_server->databaseConnectionClosed(databaseConnectionIdentifier);
+ });
+}
+
+void InProcessIDBServer::abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, transactionIdentifier] {
+ m_server->abortOpenAndUpgradeNeeded(databaseConnectionIdentifier, transactionIdentifier);
+ });
+}
+
+void InProcessIDBServer::didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier, requestIdentifier] {
+ m_server->didFireVersionChangeEvent(databaseConnectionIdentifier, requestIdentifier);
+ });
+}
+
+void InProcessIDBServer::openDBRequestCancelled(const IDBRequestData& requestData)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), requestData] {
+ m_server->openDBRequestCancelled(requestData);
+ });
+}
+
+void InProcessIDBServer::confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), databaseConnectionIdentifier] {
+ m_server->confirmDidCloseFromServer(databaseConnectionIdentifier);
+ });
+}
+
+void InProcessIDBServer::getAllDatabaseNames(const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), mainFrameOrigin, openingOrigin, callbackID] {
+ m_server->getAllDatabaseNames(m_connectionToServer->identifier(), mainFrameOrigin, openingOrigin, callbackID);
+ });
+}
+
+void InProcessIDBServer::didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames)
+{
+ RunLoop::current().dispatch([this, protectedThis = makeRef(*this), callbackID, databaseNames] {
+ m_connectionToServer->didGetAllDatabaseNames(callbackID, databaseNames);
+ });
+}
+
+void InProcessIDBServer::accessToTemporaryFileComplete(const String& path)
+{
+ deleteFile(path);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h
new file mode 100644
index 000000000..d0dc2ec02
--- /dev/null
+++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.
+ */
+
+#pragma once
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBConnectionToClient.h"
+#include "IDBConnectionToServer.h"
+#include "IDBOpenDBRequest.h"
+#include "IDBServer.h"
+#include <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+namespace IDBClient {
+class IDBConnectionToServer;
+}
+
+namespace IDBServer {
+class IDBServer;
+}
+
+class InProcessIDBServer final : public IDBClient::IDBConnectionToServerDelegate, public IDBServer::IDBConnectionToClientDelegate, public RefCounted<InProcessIDBServer>, public IDBServer::IDBBackingStoreTemporaryFileHandler {
+public:
+ WEBCORE_EXPORT static Ref<InProcessIDBServer> create();
+ WEBCORE_EXPORT static Ref<InProcessIDBServer> create(const String& databaseDirectoryPath);
+
+ WEBCORE_EXPORT IDBClient::IDBConnectionToServer& connectionToServer() const;
+ IDBServer::IDBConnectionToClient& connectionToClient() const;
+ IDBServer::IDBServer& server() { return m_server.get(); }
+
+ IDBServer::IDBServer& idbServer() { return m_server.get(); }
+
+ // IDBConnectionToServer
+ void deleteDatabase(const IDBRequestData&) final;
+ void openDatabase(const IDBRequestData&) final;
+ void abortTransaction(const IDBResourceIdentifier&) final;
+ void commitTransaction(const IDBResourceIdentifier&) final;
+ void didFinishHandlingVersionChangeTransaction(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier&) final;
+ void createObjectStore(const IDBRequestData&, const IDBObjectStoreInfo&) final;
+ void deleteObjectStore(const IDBRequestData&, const String& objectStoreName) final;
+ void renameObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& newName) final;
+ void clearObjectStore(const IDBRequestData&, uint64_t objectStoreIdentifier) final;
+ void createIndex(const IDBRequestData&, const IDBIndexInfo&) final;
+ void deleteIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, const String& indexName) final;
+ void renameIndex(const IDBRequestData&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final;
+ void putOrAdd(const IDBRequestData&, const IDBKeyData&, const IDBValue&, const IndexedDB::ObjectStoreOverwriteMode) final;
+ void getRecord(const IDBRequestData&, const IDBGetRecordData&) final;
+ void getAllRecords(const IDBRequestData&, const IDBGetAllRecordsData&) final;
+ void getCount(const IDBRequestData&, const IDBKeyRangeData&) final;
+ void deleteRecord(const IDBRequestData&, const IDBKeyRangeData&) final;
+ void openCursor(const IDBRequestData&, const IDBCursorInfo&) final;
+ void iterateCursor(const IDBRequestData&, const IDBIterateCursorData&) final;
+ void establishTransaction(uint64_t databaseConnectionIdentifier, const IDBTransactionInfo&) final;
+ void databaseConnectionPendingClose(uint64_t databaseConnectionIdentifier) final;
+ void databaseConnectionClosed(uint64_t databaseConnectionIdentifier) final;
+ void abortOpenAndUpgradeNeeded(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& transactionIdentifier) final;
+ void didFireVersionChangeEvent(uint64_t databaseConnectionIdentifier, const IDBResourceIdentifier& requestIdentifier) final;
+ void openDBRequestCancelled(const IDBRequestData&) final;
+ void confirmDidCloseFromServer(uint64_t databaseConnectionIdentifier) final;
+ void getAllDatabaseNames(const SecurityOriginData& mainFrameOrigin, const SecurityOriginData& openingOrigin, uint64_t callbackID) final;
+
+ // IDBConnectionToClient
+ uint64_t identifier() const override;
+ void didDeleteDatabase(const IDBResultData&) final;
+ void didOpenDatabase(const IDBResultData&) final;
+ void didAbortTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) final;
+ void didCommitTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) final;
+ void didCreateObjectStore(const IDBResultData&) final;
+ void didDeleteObjectStore(const IDBResultData&) final;
+ void didRenameObjectStore(const IDBResultData&) final;
+ void didClearObjectStore(const IDBResultData&) final;
+ void didCreateIndex(const IDBResultData&) final;
+ void didDeleteIndex(const IDBResultData&) final;
+ void didRenameIndex(const IDBResultData&) final;
+ void didPutOrAdd(const IDBResultData&) final;
+ void didGetRecord(const IDBResultData&) final;
+ void didGetAllRecords(const IDBResultData&) final;
+ void didGetCount(const IDBResultData&) final;
+ void didDeleteRecord(const IDBResultData&) final;
+ void didOpenCursor(const IDBResultData&) final;
+ void didIterateCursor(const IDBResultData&) final;
+ void fireVersionChangeEvent(IDBServer::UniqueIDBDatabaseConnection&, const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion) final;
+ void didStartTransaction(const IDBResourceIdentifier& transactionIdentifier, const IDBError&) final;
+ void didCloseFromServer(IDBServer::UniqueIDBDatabaseConnection&, const IDBError&) final;
+ void notifyOpenDBRequestBlocked(const IDBResourceIdentifier& requestIdentifier, uint64_t oldVersion, uint64_t newVersion) final;
+ void didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames) final;
+
+ void ref() override { RefCounted<InProcessIDBServer>::ref(); }
+ void deref() override { RefCounted<InProcessIDBServer>::deref(); }
+
+ void prepareForAccessToTemporaryFile(const String&) override { }
+ void accessToTemporaryFileComplete(const String& path) override;
+
+private:
+ InProcessIDBServer();
+ InProcessIDBServer(const String& databaseDirectoryPath);
+
+ Ref<IDBServer::IDBServer> m_server;
+ RefPtr<IDBClient::IDBConnectionToServer> m_connectionToServer;
+ RefPtr<IDBServer::IDBConnectionToClient> m_connectionToClient;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp b/Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp
index 0d14288ce..a40f90120 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp
+++ b/Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -24,54 +24,64 @@
*/
#include "config.h"
-#include "IDBDatabaseMetadata.h"
+#include "IndexKey.h"
#if ENABLE(INDEXED_DATABASE)
namespace WebCore {
-IDBDatabaseMetadata IDBDatabaseMetadata::isolatedCopy() const
+IndexKey::IndexKey()
{
- IDBDatabaseMetadata result;
- result.id = id;
- result.version = version;
- result.maxObjectStoreId = maxObjectStoreId;
+}
- result.name = name.isolatedCopy();
+IndexKey::IndexKey(Vector<IDBKeyData>&& keys)
+{
+ m_keys.swap(keys);
+}
- for (auto i = objectStores.begin(), end = objectStores.end(); i != end; ++i)
- result.objectStores.set(i->key, i->value.isolatedCopy());
+IndexKey IndexKey::isolatedCopy() const
+{
+ Vector<IDBKeyData> keys;
+ keys.reserveInitialCapacity(m_keys.size());
+ for (auto& key : m_keys)
+ keys.uncheckedAppend(key.isolatedCopy());
- return result;
+ return { WTFMove(keys) };
}
-IDBObjectStoreMetadata IDBObjectStoreMetadata::isolatedCopy() const
+IDBKeyData IndexKey::asOneKey() const
{
- IDBObjectStoreMetadata result;
- result.id = id;
- result.autoIncrement = autoIncrement;
- result.maxIndexId = maxIndexId;
+ if (m_keys.isEmpty())
+ return { };
- result.name = name.isolatedCopy();
- result.keyPath = keyPath.isolatedCopy();
-
- for (auto i = indexes.begin(), end = indexes.end(); i != end; ++i)
- result.indexes.set(i->key, i->value.isolatedCopy());
+ if (m_keys.size() == 1)
+ return m_keys[0];
+ IDBKeyData result;
+ result.setArrayValue(m_keys);
return result;
}
-IDBIndexMetadata IDBIndexMetadata::isolatedCopy() const
+Vector<IDBKeyData> IndexKey::multiEntry() const
{
- IDBIndexMetadata result;
- result.id = id;
- result.unique = unique;
- result.multiEntry = multiEntry;
-
- result.name = name.isolatedCopy();
- result.keyPath = keyPath.isolatedCopy();
-
- return result;
+ Vector<IDBKeyData> multiEntry;
+ for (auto& key : m_keys) {
+ if (!key.isValid())
+ continue;
+
+ bool skip = false;
+ for (auto& otherKey : multiEntry) {
+ if (key == otherKey) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (!skip)
+ multiEntry.append(key);
+ }
+
+ return multiEntry;
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/IDBOperation.h b/Source/WebCore/Modules/indexeddb/shared/IndexKey.h
index d88d2ce21..ad80e86f1 100644
--- a/Source/WebCore/Modules/indexeddb/IDBOperation.h
+++ b/Source/WebCore/Modules/indexeddb/shared/IndexKey.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,28 +23,32 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBOperation_h
-#define IDBOperation_h
-
-#include <functional>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "IDBKeyData.h"
+#include <wtf/HashMap.h>
+#include <wtf/Vector.h>
+
namespace WebCore {
-class IDBOperation : public RefCounted<IDBOperation> {
+class IndexKey {
public:
- virtual ~IDBOperation() { }
- virtual void perform(std::function<void()> completionCallback) = 0;
-};
+ IndexKey();
+ IndexKey(Vector<IDBKeyData>&&);
-class IDBSynchronousOperation : public RefCounted<IDBSynchronousOperation> {
-public:
- virtual ~IDBSynchronousOperation() { }
- virtual void perform() = 0;
+ IndexKey isolatedCopy() const;
+
+ IDBKeyData asOneKey() const;
+ Vector<IDBKeyData> multiEntry() const;
+
+ bool isNull() const { return m_keys.isEmpty(); }
+
+private:
+ Vector<IDBKeyData> m_keys;
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBOperation_h