summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/storage
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2011-06-02 12:07:03 +0100
committerBen Murdoch <benm@google.com>2011-06-10 10:47:21 +0100
commit2daae5fd11344eaa88a0d92b0f6d65f8d2255c00 (patch)
treee4964fbd1cb70599f7718ff03e50ea1dab33890b /Source/WebCore/storage
parent87bdf0060a247bfbe668342b87e0874182e0ffa9 (diff)
downloadexternal_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.zip
external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.gz
external_webkit-2daae5fd11344eaa88a0d92b0f6d65f8d2255c00.tar.bz2
Merge WebKit at r84325: Initial merge by git.
Change-Id: Ic1a909300ecc0a13ddc6b4e784371d2ac6e3d59b
Diffstat (limited to 'Source/WebCore/storage')
-rw-r--r--Source/WebCore/storage/IDBBackingStore.h52
-rw-r--r--Source/WebCore/storage/IDBDatabaseBackendImpl.cpp12
-rw-r--r--Source/WebCore/storage/IDBFactoryBackendImpl.cpp12
-rw-r--r--Source/WebCore/storage/IDBIndexBackendImpl.cpp20
-rw-r--r--Source/WebCore/storage/IDBIndexBackendImpl.h16
-rw-r--r--Source/WebCore/storage/IDBLevelDBBackingStore.cpp2613
-rw-r--r--Source/WebCore/storage/IDBLevelDBBackingStore.h91
-rw-r--r--Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp81
-rw-r--r--Source/WebCore/storage/IDBObjectStoreBackendImpl.h16
-rw-r--r--Source/WebCore/storage/IDBSQLiteBackingStore.cpp116
-rw-r--r--Source/WebCore/storage/IDBSQLiteBackingStore.h47
-rw-r--r--Source/WebCore/storage/StorageInfo.cpp68
-rw-r--r--Source/WebCore/storage/StorageInfo.h72
-rw-r--r--Source/WebCore/storage/StorageInfo.idl37
-rw-r--r--Source/WebCore/storage/StorageInfoErrorCallback.h52
-rw-r--r--Source/WebCore/storage/StorageInfoErrorCallback.idl38
-rw-r--r--Source/WebCore/storage/StorageInfoQuotaCallback.h50
-rw-r--r--Source/WebCore/storage/StorageInfoUsageCallback.h50
-rw-r--r--Source/WebCore/storage/StorageInfoUsageCallback.idl39
-rw-r--r--Source/WebCore/storage/StorageTracker.cpp31
-rw-r--r--Source/WebCore/storage/StorageTracker.h1
21 files changed, 3356 insertions, 158 deletions
diff --git a/Source/WebCore/storage/IDBBackingStore.h b/Source/WebCore/storage/IDBBackingStore.h
index 29523a2..4b96442 100644
--- a/Source/WebCore/storage/IDBBackingStore.h
+++ b/Source/WebCore/storage/IDBBackingStore.h
@@ -50,30 +50,38 @@ public:
virtual bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId) = 0;
virtual void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags) = 0;
- virtual bool createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId) = 0;
- virtual void deleteObjectStore(int64_t objectStoreId) = 0;
- virtual String getObjectStoreRecord(int64_t objectStoreId, const IDBKey&) = 0;
- virtual bool putObjectStoreRecord(int64_t objectStoreId, const IDBKey&, const String& value, int64_t& rowId, bool invalidRowId) = 0;
- virtual void clearObjectStore(int64_t objectStoreId) = 0;
- virtual void deleteObjectStoreRecord(int64_t objectStoreId, int64_t objectStoreDataId) = 0;
- virtual double nextAutoIncrementNumber(int64_t objectStoreId) = 0;
- virtual bool keyExistsInObjectStore(int64_t objectStoreId, const IDBKey&, int64_t& foundObjectStoreDataId) = 0;
+ virtual bool createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId) = 0;
+ virtual void deleteObjectStore(int64_t databaseId, int64_t objectStoreId) = 0;
+
+ class ObjectStoreRecordIdentifier : public RefCounted<ObjectStoreRecordIdentifier> {
+ public:
+ virtual bool isValid() const = 0;
+ virtual ~ObjectStoreRecordIdentifier() {}
+ };
+ virtual PassRefPtr<ObjectStoreRecordIdentifier> createInvalidRecordIdentifier() = 0;
+
+ virtual String getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&) = 0;
+ virtual bool putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&, const String& value, ObjectStoreRecordIdentifier*) = 0;
+ virtual void clearObjectStore(int64_t databaseId, int64_t objectStoreId) = 0;
+ virtual void deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier*) = 0;
+ virtual double nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId) = 0;
+ virtual bool keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey&, ObjectStoreRecordIdentifier* foundRecordIdentifier) = 0;
class ObjectStoreRecordCallback {
public:
- virtual bool callback(int64_t objectStoreDataId, const String& value) = 0;
+ virtual bool callback(const ObjectStoreRecordIdentifier*, const String& value) = 0;
virtual ~ObjectStoreRecordCallback() {};
};
- virtual bool forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback&) = 0;
+ virtual bool forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback&) = 0;
- virtual void getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) = 0;
- virtual bool createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) = 0;
- virtual void deleteIndex(int64_t indexId) = 0;
- virtual bool putIndexDataForRecord(int64_t indexId, const IDBKey&, int64_t objectStoreDataId) = 0;
- virtual bool deleteIndexDataForRecord(int64_t objectStoreDataId) = 0;
- virtual String getObjectViaIndex(int64_t indexId, const IDBKey&) = 0;
- virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t indexId, const IDBKey&) = 0;
- virtual bool keyExistsInIndex(int64_t indexId, const IDBKey&) = 0;
+ virtual void getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags) = 0;
+ virtual bool createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId) = 0;
+ virtual void deleteIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId) = 0;
+ virtual bool putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const ObjectStoreRecordIdentifier*) = 0;
+ virtual bool deleteIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const ObjectStoreRecordIdentifier*) = 0;
+ virtual String getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&) = 0;
+ virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&) = 0;
+ virtual bool keyExistsInIndex(int64_t databaseid, int64_t objectStoreId, int64_t indexId, const IDBKey&) = 0;
class Cursor : public RefCounted<Cursor> {
public:
@@ -81,14 +89,14 @@ public:
virtual PassRefPtr<IDBKey> key() = 0;
virtual PassRefPtr<IDBKey> primaryKey() = 0;
virtual String value() = 0;
- virtual int64_t objectStoreDataId() = 0;
+ virtual PassRefPtr<ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() = 0;
virtual int64_t indexDataId() = 0;
virtual ~Cursor() {};
};
- virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction) = 0;
- virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0;
- virtual PassRefPtr<Cursor> openIndexCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0;
+ virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction) = 0;
+ virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0;
+ virtual PassRefPtr<Cursor> openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction) = 0;
class Transaction : public RefCounted<Transaction> {
public:
diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp
index 94e305b..8fe971a 100644
--- a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -97,7 +97,7 @@ PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const
return objectStoreNames.release();
}
-PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
+PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
{
ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
@@ -106,7 +106,7 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObject
return 0;
}
- RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), name, keyPath, autoIncrement);
+ RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, name, keyPath, autoIncrement);
ASSERT(objectStore->name() == name);
RefPtr<IDBDatabaseBackendImpl> database = this;
@@ -125,7 +125,7 @@ void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*,
{
int64_t objectStoreId;
- if (!database->m_backingStore->createObjectStore(objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), database->id(), objectStoreId)) {
+ if (!database->m_backingStore->createObjectStore(database->id(), objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), objectStoreId)) {
transaction->abort();
return;
}
@@ -158,7 +158,7 @@ void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactio
void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- database->m_backingStore->deleteObjectStore(objectStore->id());
+ database->m_backingStore->deleteObjectStore(database->id(), objectStore->id());
transaction->didCompleteTaskEvents();
}
@@ -250,7 +250,7 @@ void IDBDatabaseBackendImpl::loadObjectStores()
ASSERT(autoIncrementFlags.size() == ids.size());
for (size_t i = 0; i < ids.size(); i++)
- m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), ids[i], names[i], keyPaths[i], autoIncrementFlags[i]));
+ m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), m_id, ids[i], names[i], keyPaths[i], autoIncrementFlags[i]));
}
void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp
index 461e930..4768b3e 100644
--- a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -32,6 +32,7 @@
#include "DOMStringList.h"
#include "IDBDatabaseBackendImpl.h"
#include "IDBDatabaseException.h"
+#include "IDBLevelDBBackingStore.h"
#include "IDBSQLiteBackingStore.h"
#include "IDBTransactionCoordinator.h"
#include "SecurityOrigin.h"
@@ -69,7 +70,7 @@ void IDBFactoryBackendImpl::removeIDBBackingStore(const String& uniqueIdentifier
m_backingStoreMap.remove(uniqueIdentifier);
}
-void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDir, int64_t maximumSize, BackingStoreType)
+void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDir, int64_t maximumSize, BackingStoreType backingStoreType)
{
String fileIdentifier = securityOrigin->databaseIdentifier();
String uniqueIdentifier = fileIdentifier + "@" + name;
@@ -86,7 +87,12 @@ void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> ca
if (it2 != m_backingStoreMap.end())
backingStore = it2->second;
else {
- backingStore = IDBSQLiteBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this);
+ if (backingStoreType == DefaultBackingStore)
+ backingStore = IDBSQLiteBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this);
+#if ENABLE(LEVELDB)
+ else if (backingStoreType == LevelDBBackingStore)
+ backingStore = IDBLevelDBBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this);
+#endif
if (!backingStore) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
return;
diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.cpp b/Source/WebCore/storage/IDBIndexBackendImpl.cpp
index 62e5364..38f95f1 100644
--- a/Source/WebCore/storage/IDBIndexBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBIndexBackendImpl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -40,8 +40,10 @@
namespace WebCore {
-IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
+IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
: m_backingStore(backingStore)
+ , m_databaseId(databaseId)
+ , m_objectStoreBackend(objectStoreBackend)
, m_id(id)
, m_name(name)
, m_storeName(storeName)
@@ -50,8 +52,10 @@ IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t
{
}
-IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique)
+IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, const String& name, const String& storeName, const String& keyPath, bool unique)
: m_backingStore(backingStore)
+ , m_databaseId(databaseId)
+ , m_objectStoreBackend(objectStoreBackend)
, m_id(InvalidId)
, m_name(name)
, m_storeName(storeName)
@@ -72,10 +76,10 @@ void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr
switch (cursorType) {
case IDBCursorBackendInterface::IndexKeyCursor:
- backingStoreCursor = index->m_backingStore->openIndexKeyCursor(index->id(), range.get(), direction);
+ backingStoreCursor = index->m_backingStore->openIndexKeyCursor(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), range.get(), direction);
break;
case IDBCursorBackendInterface::IndexCursor:
- backingStoreCursor = index->m_backingStore->openIndexCursor(index->id(), range.get(), direction);
+ backingStoreCursor = index->m_backingStore->openIndexCursor(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), range.get(), direction);
break;
case IDBCursorBackendInterface::ObjectStoreCursor:
case IDBCursorBackendInterface::InvalidCursorType:
@@ -120,14 +124,14 @@ void IDBIndexBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBInd
{
// FIXME: Split getInternal into two functions, getting rid off |getObject|.
if (getObject) {
- String value = index->m_backingStore->getObjectViaIndex(index->id(), *key);
+ String value = index->m_backingStore->getObjectViaIndex(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), *key);
if (value.isNull()) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
return;
}
callbacks->onSuccess(SerializedScriptValue::createFromWire(value));
} else {
- RefPtr<IDBKey> keyResult = index->m_backingStore->getPrimaryKeyViaIndex(index->id(), *key);
+ RefPtr<IDBKey> keyResult = index->m_backingStore->getPrimaryKeyViaIndex(index->m_databaseId, index->m_objectStoreBackend->id(), index->id(), *key);
if (!keyResult) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
return;
@@ -159,7 +163,7 @@ bool IDBIndexBackendImpl::addingKeyAllowed(IDBKey* key)
if (!m_unique)
return true;
- return !m_backingStore->keyExistsInIndex(m_id, *key);
+ return !m_backingStore->keyExistsInIndex(m_databaseId, m_objectStoreBackend->id(), m_id, *key);
}
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.h b/Source/WebCore/storage/IDBIndexBackendImpl.h
index e2a06b8..2d42a08 100644
--- a/Source/WebCore/storage/IDBIndexBackendImpl.h
+++ b/Source/WebCore/storage/IDBIndexBackendImpl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -40,13 +40,13 @@ class ScriptExecutionContext;
class IDBIndexBackendImpl : public IDBIndexBackendInterface {
public:
- static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
+ static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
{
- return adoptRef(new IDBIndexBackendImpl(backingStore, id, name, storeName, keyPath, unique));
+ return adoptRef(new IDBIndexBackendImpl(backingStore, databaseId, objectStoreBackend, id, name, storeName, keyPath, unique));
}
- static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique)
+ static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, const IDBObjectStoreBackendImpl* objectStoreBackend, const String& name, const String& storeName, const String& keyPath, bool unique)
{
- return adoptRef(new IDBIndexBackendImpl(backingStore, name, storeName, keyPath, unique));
+ return adoptRef(new IDBIndexBackendImpl(backingStore, databaseId, objectStoreBackend, name, storeName, keyPath, unique));
}
virtual ~IDBIndexBackendImpl();
@@ -72,8 +72,8 @@ public:
virtual void getKey(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&);
private:
- IDBIndexBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique);
- IDBIndexBackendImpl(IDBBackingStore*, const String& name, const String& storeName, const String& keyPath, bool unique);
+ IDBIndexBackendImpl(IDBBackingStore*, int64_t databaseId, const IDBObjectStoreBackendImpl*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique);
+ IDBIndexBackendImpl(IDBBackingStore*, int64_t databaseId, const IDBObjectStoreBackendImpl*, const String& name, const String& storeName, const String& keyPath, bool unique);
static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, IDBCursorBackendInterface::CursorType, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>);
static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKey>, bool getObject, PassRefPtr<IDBCallbacks>);
@@ -82,6 +82,8 @@ private:
RefPtr<IDBBackingStore> m_backingStore;
+ int64_t m_databaseId;
+ const IDBObjectStoreBackendImpl* m_objectStoreBackend;
int64_t m_id;
String m_name;
String m_storeName;
diff --git a/Source/WebCore/storage/IDBLevelDBBackingStore.cpp b/Source/WebCore/storage/IDBLevelDBBackingStore.cpp
new file mode 100644
index 0000000..09d667f
--- /dev/null
+++ b/Source/WebCore/storage/IDBLevelDBBackingStore.cpp
@@ -0,0 +1,2613 @@
+/*
+ * 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 "IDBLevelDBBackingStore.h"
+
+#if ENABLE(INDEXED_DATABASE)
+#if ENABLE(LEVELDB)
+
+#include "Assertions.h"
+#include "FileSystem.h"
+#include "IDBFactoryBackendImpl.h"
+#include "IDBKeyRange.h"
+#include "LevelDBComparator.h"
+#include "LevelDBDatabase.h"
+#include "LevelDBIterator.h"
+#include "LevelDBSlice.h"
+#include "SecurityOrigin.h"
+
+#ifndef INT64_MAX
+// FIXME: We shouldn't need to rely on these macros.
+#define INT64_MAX 0x7fffffffffffffffLL
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffffL
+#endif
+
+// 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 (0 for now) [SchemaVersionKey]
+// <0, 0, 0, 1> => The maximum database id ever allocated [MaxDatabaseIdKey]
+// <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]
+//
+//
+// Object store meta-data:
+//
+// The prefix is followed by a type byte, then a variable-length integer, and then another variable-length integer (FIXME: this should be a 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]
+//
+//
+// 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]
+//
+//
+// 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 user key is an encoded IDBKey. The sequence number is a variable length integer.
+//
+// <database id, object store id, index id, user key, sequence number> => "version", user key [IndexDataKey]
+//
+// (The sequence number is used to allow two entries with the same user key
+// in non-unique indexes. 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).
+
+static const unsigned char kIDBKeyNullTypeByte = 0;
+static const unsigned char kIDBKeyStringTypeByte = 1;
+static const unsigned char kIDBKeyDateTypeByte = 2;
+static const unsigned char kIDBKeyNumberTypeByte = 3;
+static const unsigned char kIDBKeyMinKeyTypeByte = 4;
+
+static const unsigned char kMinimumIndexId = 30;
+static const unsigned char kObjectStoreDataIndexId = 1;
+static const unsigned char kExistsEntryIndexId = 2;
+
+static const unsigned char kSchemaVersionTypeByte = 0;
+static const unsigned char kMaxDatabaseIdTypeByte = 1;
+static const unsigned char kDatabaseFreeListTypeByte = 100;
+static const unsigned char kDatabaseNameTypeByte = 201;
+
+static const unsigned char kObjectStoreMetaDataTypeByte = 50;
+static const unsigned char kIndexMetaDataTypeByte = 100;
+static const unsigned char kObjectStoreFreeListTypeByte = 150;
+static const unsigned char kIndexFreeListTypeByte = 151;
+static const unsigned char kObjectStoreNamesTypeByte = 200;
+static const unsigned char kIndexNamesKeyTypeByte = 201;
+
+namespace WebCore {
+
+static Vector<char> encodeByte(unsigned char c)
+{
+ Vector<char> v;
+ v.append(c);
+ return v;
+}
+
+static Vector<char> maxIDBKey()
+{
+ return encodeByte(kIDBKeyNullTypeByte);
+}
+
+static Vector<char> minIDBKey()
+{
+ return encodeByte(kIDBKeyMinKeyTypeByte);
+}
+
+static Vector<char> encodeInt(int64_t n)
+{
+ ASSERT(n >= 0);
+ Vector<char> ret; // FIXME: Size this at creation.
+
+ do {
+ unsigned char c = n;
+ ret.append(c);
+ n >>= 8;
+ } while (n);
+
+ return ret;
+}
+
+static int64_t decodeInt(const char* begin, const char* end)
+{
+ ASSERT(begin <= end);
+ int64_t ret = 0;
+
+ while (begin < end) {
+ unsigned char c = *begin++;
+ ret = (ret << 8) | c;
+ }
+
+ return ret;
+}
+
+static Vector<char> encodeVarInt(int64_t n)
+{
+ Vector<char> ret; // FIXME: Size this at creation.
+
+ do {
+ unsigned char c = n & 0x7f;
+ n >>= 7;
+ if (n)
+ c |= 128;
+ ret.append(c);
+ } while (n);
+
+ return ret;
+}
+
+static const char* decodeVarInt(const char *p, const char* limit, int64_t& foundInt)
+{
+ ASSERT(limit >= p);
+ foundInt = 0;
+
+ do {
+ if (p >= limit)
+ return 0;
+
+ foundInt = (foundInt << 7) | (*p & 0x7f);
+ } while (*p++ & 128);
+ return p;
+}
+
+static Vector<char> encodeString(const String& s)
+{
+ Vector<char> ret; // FIXME: Size this at creation.
+
+ for (unsigned i = 0; i < s.length(); ++i) {
+ UChar u = s[i];
+ unsigned char hi = u >> 8;
+ unsigned char lo = u;
+ ret.append(hi);
+ ret.append(lo);
+ }
+
+ return ret;
+}
+
+static String decodeString(const char* p, const char* end)
+{
+ ASSERT(end >= p);
+ ASSERT(!((end - p) % 2));
+
+ size_t len = (end - p) / 2;
+ Vector<UChar> vector(len);
+
+ for (size_t i = 0; i < len; ++i) {
+ unsigned char hi = *p++;
+ unsigned char lo = *p++;
+
+ vector[i] = (hi << 8) | lo;
+ }
+
+ return String::adopt(vector);
+}
+
+static Vector<char> encodeStringWithLength(const String& s)
+{
+ Vector<char> ret = encodeVarInt(s.length());
+ ret.append(encodeString(s));
+ return ret;
+}
+
+static const char* decodeStringWithLength(const char* p, const char* limit, String& foundString)
+{
+ ASSERT(limit >= p);
+ int64_t len;
+ p = decodeVarInt(p, limit, len);
+ if (!p)
+ return 0;
+ if (p + len * 2 > limit)
+ return 0;
+
+ foundString = decodeString(p, p + len * 2);
+ p += len * 2;
+ return p;
+}
+
+static 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> v;
+ v.append(p, sizeof(x));
+ ASSERT(v.size() == sizeof(x));
+ return v;
+}
+
+static 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;
+}
+
+static Vector<char> encodeIDBKey(const IDBKey& key)
+{
+ Vector<char> ret;
+
+ switch (key.type()) {
+ case IDBKey::NullType:
+ return encodeByte(kIDBKeyNullTypeByte);
+ case IDBKey::StringType:
+ ret = encodeByte(kIDBKeyStringTypeByte);
+ ret.append(encodeStringWithLength(key.string()));
+ return ret;
+ case IDBKey::DateType:
+ ret = encodeByte(kIDBKeyDateTypeByte);
+ ret.append(encodeDouble(key.date()));
+ ASSERT(ret.size() == 9);
+ return ret;
+ case IDBKey::NumberType:
+ ret = encodeByte(kIDBKeyNumberTypeByte);
+ ret.append(encodeDouble(key.number()));
+ ASSERT(ret.size() == 9);
+ return ret;
+ }
+
+ ASSERT_NOT_REACHED();
+ return Vector<char>();
+}
+
+static const char* decodeIDBKey(const char* p, const char* limit, RefPtr<IDBKey>& foundKey)
+{
+ ASSERT(limit >= p);
+ if (p >= limit)
+ return 0;
+
+ unsigned char type = *p++;
+ String s;
+ double d;
+
+ switch (type) {
+ case kIDBKeyNullTypeByte:
+ // Null.
+ foundKey = IDBKey::createNull();
+ return p;
+ case kIDBKeyStringTypeByte:
+ // String.
+ p = decodeStringWithLength(p, limit, s);
+ if (!p)
+ return 0;
+ foundKey = IDBKey::createString(s);
+ return p;
+ case kIDBKeyDateTypeByte:
+ // Date.
+ p = decodeDouble(p, limit, &d);
+ if (!p)
+ return 0;
+ foundKey = IDBKey::createDate(d);
+ return p;
+ case kIDBKeyNumberTypeByte:
+ // Number.
+ p = decodeDouble(p, limit, &d);
+ if (!p)
+ return 0;
+ foundKey = IDBKey::createNumber(d);
+ return p;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static const char* extractEncodedIDBKey(const char* start, const char* limit, Vector<char>* result)
+{
+ const char* p = start;
+ if (p >= limit)
+ return 0;
+
+ unsigned char type = *p++;
+
+ int64_t stringLen;
+
+ switch (type) {
+ case kIDBKeyNullTypeByte:
+ case kIDBKeyMinKeyTypeByte:
+ *result = encodeByte(type);
+ return p;
+ case kIDBKeyStringTypeByte:
+ // String.
+ p = decodeVarInt(p, limit, stringLen);
+ if (!p)
+ return 0;
+ if (p + stringLen * 2 > limit)
+ return 0;
+ result->clear();
+ result->append(start, p - start + stringLen * 2);
+ return p + stringLen * 2;
+ case kIDBKeyDateTypeByte:
+ case kIDBKeyNumberTypeByte:
+ // Date or number.
+ if (p + sizeof(double) > limit)
+ return 0;
+ result->clear();
+ result->append(start, 1 + sizeof(double));
+ return p + sizeof(double);
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static int compareEncodedIDBKeys(const Vector<char>& keyA, const Vector<char>& keyB)
+{
+ ASSERT(keyA.size() >= 1);
+ ASSERT(keyB.size() >= 1);
+
+ const char* p = keyA.data();
+ const char* limitA = p + keyA.size();
+ const char* q = keyB.data();
+ const char* limitB = q + keyB.size();
+
+ unsigned char typeA = *p++;
+ unsigned char typeB = *q++;
+
+ String s, t;
+ double d, e;
+
+ if (int x = typeB - typeA) // FIXME: Note the subtleness!
+ return x;
+
+ switch (typeA) {
+ case kIDBKeyNullTypeByte:
+ case kIDBKeyMinKeyTypeByte:
+ // Null type or max type; no payload to compare.
+ return 0;
+ case kIDBKeyStringTypeByte:
+ // String type.
+ p = decodeStringWithLength(p, limitA, s); // FIXME: Compare without actually decoding the String!
+ ASSERT(p);
+ q = decodeStringWithLength(q, limitB, t);
+ ASSERT(q);
+ return codePointCompare(s, t);
+ case kIDBKeyDateTypeByte:
+ case kIDBKeyNumberTypeByte:
+ // Date or number.
+ p = decodeDouble(p, limitA, &d);
+ ASSERT(p);
+ q = decodeDouble(q, limitB, &e);
+ ASSERT(q);
+ if (d < e)
+ return -1;
+ if (d > e)
+ return 1;
+ return 0;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static bool getInt(LevelDBDatabase* db, const Vector<char>& key, int64_t& foundInt)
+{
+ Vector<char> result;
+ if (!db->get(key, result))
+ return false;
+
+ foundInt = decodeInt(result.begin(), result.end());
+ return true;
+}
+
+static bool putInt(LevelDBDatabase* db, const Vector<char>& key, int64_t value)
+{
+ return db->put(key, encodeInt(value));
+}
+
+static bool getString(LevelDBDatabase* db, const Vector<char>& key, String& foundString)
+{
+ Vector<char> result;
+ if (!db->get(key, result))
+ return false;
+
+ foundString = decodeString(result.begin(), result.end());
+ return true;
+}
+
+static bool putString(LevelDBDatabase* db, const Vector<char> key, const String& value)
+{
+ if (!db->put(key, encodeString(value)))
+ return false;
+ return true;
+}
+
+namespace {
+class KeyPrefix {
+public:
+ KeyPrefix()
+ : m_databaseId(kInvalidType)
+ , m_objectStoreId(kInvalidType)
+ , m_indexId(kInvalidType)
+ {
+ }
+
+ KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
+ : m_databaseId(databaseId)
+ , m_objectStoreId(objectStoreId)
+ , m_indexId(indexId)
+ {
+ }
+
+ static const char* 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> encode() const
+ {
+ ASSERT(m_databaseId != kInvalidId);
+ ASSERT(m_objectStoreId != kInvalidId);
+ ASSERT(m_indexId != kInvalidId);
+
+ Vector<char> databaseIdString = encodeInt(m_databaseId);
+ Vector<char> objectStoreIdString = encodeInt(m_objectStoreId);
+ Vector<char> indexIdString = encodeInt(m_indexId);
+
+ ASSERT(databaseIdString.size() <= 8);
+ ASSERT(objectStoreIdString.size() <= 8);
+ ASSERT(indexIdString.size() <= 4);
+
+
+ unsigned char firstByte = (databaseIdString.size() - 1) << 5 | (objectStoreIdString.size() - 1) << 2 | (indexIdString.size() - 1);
+ Vector<char> ret;
+ ret.append(firstByte);
+ ret.append(databaseIdString);
+ ret.append(objectStoreIdString);
+ ret.append(indexIdString);
+
+ return ret;
+ }
+
+ int compare(const KeyPrefix& other) const
+ {
+ ASSERT(m_databaseId != kInvalidId);
+ ASSERT(m_objectStoreId != kInvalidId);
+ ASSERT(m_indexId != kInvalidId);
+
+ if (m_databaseId != other.m_databaseId)
+ return m_databaseId - other.m_databaseId;
+ if (m_objectStoreId != other.m_objectStoreId)
+ return m_objectStoreId - other.m_objectStoreId;
+ if (m_indexId != other.m_indexId)
+ return m_indexId - other.m_indexId;
+ return 0;
+ }
+
+ enum Type {
+ kGlobalMetaData,
+ kDatabaseMetaData,
+ kObjectStoreData,
+ kExistsEntry,
+ kIndexData,
+ kInvalidType
+ };
+
+ Type type() const
+ {
+ ASSERT(m_databaseId != kInvalidId);
+ ASSERT(m_objectStoreId != kInvalidId);
+ ASSERT(m_indexId != kInvalidId);
+
+ if (!m_databaseId)
+ return kGlobalMetaData;
+ if (!m_objectStoreId)
+ return kDatabaseMetaData;
+ if (m_indexId == kObjectStoreDataIndexId)
+ return kObjectStoreData;
+ if (m_indexId == kExistsEntryIndexId)
+ return kExistsEntry;
+ if (m_indexId >= kMinimumIndexId)
+ return kIndexData;
+
+ ASSERT_NOT_REACHED();
+ return kInvalidType;
+ }
+
+ int64_t m_databaseId;
+ int64_t m_objectStoreId;
+ int64_t m_indexId;
+
+ static const int64_t kInvalidId = -1;
+};
+
+class SchemaVersionKey {
+public:
+ static Vector<char> encode()
+ {
+ KeyPrefix prefix(0, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kSchemaVersionTypeByte));
+ return ret;
+ }
+};
+
+class MaxDatabaseIdKey {
+public:
+ static Vector<char> encode()
+ {
+ KeyPrefix prefix(0, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kMaxDatabaseIdTypeByte));
+ return ret;
+ }
+};
+
+class DatabaseFreeListKey {
+public:
+ DatabaseFreeListKey()
+ : m_databaseId(-1)
+ {
+ }
+
+ static const char* 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kDatabaseFreeListTypeByte);
+ if (p == limit)
+ return 0;
+ return decodeVarInt(p, limit, result->m_databaseId);
+ }
+
+ static Vector<char> encode(int64_t databaseId)
+ {
+ KeyPrefix prefix(0, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kDatabaseFreeListTypeByte));
+ ret.append(encodeVarInt(databaseId));
+ return ret;
+ }
+
+ int64_t databaseId() const
+ {
+ ASSERT(m_databaseId >= 0);
+ return m_databaseId;
+ }
+
+ int compare(const DatabaseFreeListKey& other) const
+ {
+ ASSERT(m_databaseId >= 0);
+ return m_databaseId - other.m_databaseId;
+ }
+
+private:
+ int64_t m_databaseId;
+};
+
+class DatabaseNameKey {
+public:
+ static const char* 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kDatabaseNameTypeByte);
+ if (p == limit)
+ return 0;
+ p = decodeStringWithLength(p, limit, result->m_origin);
+ if (!p)
+ return 0;
+ return decodeStringWithLength(p, limit, result->m_databaseName);
+ }
+
+ static Vector<char> encode(const String& origin, const String& databaseName)
+ {
+ KeyPrefix prefix(0, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kDatabaseNameTypeByte));
+ ret.append(encodeStringWithLength(origin));
+ ret.append(encodeStringWithLength(databaseName));
+ return ret;
+ }
+
+ String origin() const { return m_origin; }
+ String databaseName() const { return m_databaseName; }
+
+ int compare(const DatabaseNameKey& other)
+ {
+ if (int x = codePointCompare(m_origin, other.m_origin))
+ return x;
+ return codePointCompare(m_databaseName, other.m_databaseName);
+ }
+
+private:
+ String m_origin; // FIXME: Store encoded strings, or just pointers.
+ String m_databaseName;
+};
+
+class DatabaseMetaDataKey {
+public:
+ enum MetaDataType {
+ kOriginName = 0,
+ kDatabaseName = 1,
+ kUserVersion = 2,
+ kMaxObjectStoreId = 3
+ };
+
+ static Vector<char> encode(int64_t databaseId, MetaDataType metaDataType)
+ {
+ KeyPrefix prefix(databaseId, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(metaDataType));
+ return ret;
+ }
+};
+
+class ObjectStoreMetaDataKey {
+public:
+ ObjectStoreMetaDataKey()
+ : m_objectStoreId(-1)
+ , m_metaDataType(-1)
+ {
+ }
+
+ static const char* 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kObjectStoreMetaDataTypeByte);
+ 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 decodeVarInt(p, limit, result->m_metaDataType);
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t metaDataType)
+ {
+ KeyPrefix prefix(databaseId, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kObjectStoreMetaDataTypeByte));
+ ret.append(encodeVarInt(objectStoreId));
+ ret.append(encodeVarInt(metaDataType));
+ return ret;
+ }
+
+ int64_t objectStoreId() const
+ {
+ ASSERT(m_objectStoreId >= 0);
+ return m_objectStoreId;
+ }
+ int64_t metaDataType() const
+ {
+ ASSERT(m_metaDataType >= 0);
+ return m_metaDataType;
+ }
+
+ int compare(const ObjectStoreMetaDataKey& other)
+ {
+ ASSERT(m_objectStoreId >= 0);
+ ASSERT(m_metaDataType >= 0);
+ if (int x = m_objectStoreId - other.m_objectStoreId)
+ return x; // FIXME: Is this cast safe? I.e., will it preserve the sign?
+ return m_metaDataType - other.m_metaDataType;
+ }
+
+private:
+ int64_t m_objectStoreId;
+ int64_t m_metaDataType; // FIXME: Make this a byte.
+};
+
+class IndexMetaDataKey {
+public:
+ IndexMetaDataKey()
+ : m_objectStoreId(-1)
+ , m_indexId(-1)
+ , m_metaDataType(0)
+ {
+ }
+
+ static const char* 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kIndexMetaDataTypeByte);
+ 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;
+ result->m_metaDataType = *p++;
+ return p;
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType)
+ {
+ KeyPrefix prefix(databaseId, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kIndexMetaDataTypeByte));
+ ret.append(encodeVarInt(objectStoreId));
+ ret.append(encodeVarInt(indexId));
+ ret.append(encodeByte(metaDataType));
+ return ret;
+ }
+
+ int compare(const IndexMetaDataKey& other)
+ {
+ ASSERT(m_objectStoreId >= 0);
+ ASSERT(m_indexId >= 0);
+
+ if (int x = m_objectStoreId - other.m_objectStoreId)
+ return x;
+ if (int x = m_indexId - other.m_indexId)
+ return x;
+ return m_metaDataType - other.m_metaDataType;
+ }
+
+ int64_t indexId() const
+ {
+ ASSERT(m_indexId >= 0);
+ return m_indexId;
+ }
+
+ unsigned char metaDataType() const { return m_metaDataType; }
+
+private:
+ int64_t m_objectStoreId;
+ int64_t m_indexId;
+ unsigned char m_metaDataType;
+};
+
+class ObjectStoreFreeListKey {
+public:
+ ObjectStoreFreeListKey()
+ : m_objectStoreId(-1)
+ {
+ }
+
+ static const char* 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kObjectStoreFreeListTypeByte);
+ if (p == limit)
+ return 0;
+ return decodeVarInt(p, limit, result->m_objectStoreId);
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId)
+ {
+ KeyPrefix prefix(databaseId, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kObjectStoreFreeListTypeByte));
+ ret.append(encodeVarInt(objectStoreId));
+ return ret;
+ }
+
+ int64_t objectStoreId() const
+ {
+ ASSERT(m_objectStoreId >= 0);
+ return m_objectStoreId;
+ }
+
+ int 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 m_objectStoreId - other.m_objectStoreId;
+ }
+
+private:
+ int64_t m_objectStoreId;
+};
+
+class IndexFreeListKey {
+public:
+ IndexFreeListKey()
+ : m_objectStoreId(-1)
+ , m_indexId(-1)
+ {
+ }
+
+ static const char* 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kIndexFreeListTypeByte);
+ if (p == limit)
+ return 0;
+ p = decodeVarInt(p, limit, result->m_objectStoreId);
+ if (!p)
+ return 0;
+ return decodeVarInt(p, limit, result->m_indexId);
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
+ {
+ KeyPrefix prefix(databaseId, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kIndexFreeListTypeByte));
+ ret.append(encodeVarInt(objectStoreId));
+ ret.append(encodeVarInt(indexId));
+ return ret;
+ }
+
+ int compare(const IndexFreeListKey& other)
+ {
+ ASSERT(m_objectStoreId >= 0);
+ ASSERT(m_indexId >= 0);
+ if (int x = m_objectStoreId - other.m_objectStoreId)
+ return x;
+ return m_indexId - other.m_indexId;
+ }
+
+ int64_t objectStoreId() const
+ {
+ ASSERT(m_objectStoreId >= 0);
+ return m_objectStoreId;
+ }
+
+ int64_t indexId() const
+ {
+ ASSERT(m_indexId >= 0);
+ return m_indexId;
+ }
+
+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 IDBDatabaseBackendImpl. Can the mapping become unreliable?
+ // Can we remove this?
+ static const char* 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kObjectStoreNamesTypeByte);
+ return decodeStringWithLength(p, limit, result->m_objectStoreName);
+ }
+
+ static Vector<char> encode(int64_t databaseId, const String& objectStoreName)
+ {
+ KeyPrefix prefix(databaseId, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kSchemaVersionTypeByte));
+ ret.append(encodeByte(kObjectStoreNamesTypeByte));
+ ret.append(encodeStringWithLength(objectStoreName));
+ return ret;
+ }
+
+ int compare(const ObjectStoreNamesKey& other)
+ {
+ return codePointCompare(m_objectStoreName, other.m_objectStoreName);
+ }
+
+ String objectStoreName() const { return m_objectStoreName; }
+
+private:
+ String m_objectStoreName; // FIXME: Store the encoded string, or just pointers to it.
+};
+
+class IndexNamesKey {
+public:
+ IndexNamesKey()
+ : m_objectStoreId(-1)
+ {
+ }
+
+ // 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)
+ {
+ 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 = *p++;
+ ASSERT_UNUSED(typeByte, typeByte == kIndexNamesKeyTypeByte);
+ if (p == limit)
+ return 0;
+ p = decodeVarInt(p, limit, result->m_objectStoreId);
+ if (!p)
+ return 0;
+ return decodeStringWithLength(p, limit, result->m_indexName);
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const String& indexName)
+ {
+ KeyPrefix prefix(databaseId, 0, 0);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodeByte(kIndexNamesKeyTypeByte));
+ ret.append(encodeVarInt(objectStoreId));
+ ret.append(encodeStringWithLength(indexName));
+ return ret;
+ }
+
+ int compare(const IndexNamesKey& other)
+ {
+ ASSERT(m_objectStoreId >= 0);
+ if (int x = m_objectStoreId - other.m_objectStoreId)
+ return x;
+ return codePointCompare(m_indexName, other.m_indexName);
+ }
+
+ 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)
+ {
+ 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 == kSpecialIndexNumber);
+ if (p == end)
+ return 0;
+ return extractEncodedIDBKey(p, end, &result->m_encodedUserKey);
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char> encodedUserKey)
+ {
+ KeyPrefix prefix(databaseId, objectStoreId, kSpecialIndexNumber);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodedUserKey);
+
+ return ret;
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey)
+ {
+ return encode(databaseId, objectStoreId, encodeIDBKey(userKey));
+ }
+
+ int compare(const ObjectStoreDataKey& other)
+ {
+ return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey);
+ }
+
+ PassRefPtr<IDBKey> userKey() const
+ {
+ RefPtr<IDBKey> key;
+ decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key);
+ return key;
+ }
+
+ static const int64_t kSpecialIndexNumber = kObjectStoreDataIndexId;
+
+private:
+ Vector<char> m_encodedUserKey;
+};
+
+class ExistsEntryKey {
+public:
+ static const char* 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 == kSpecialIndexNumber);
+ if (p == end)
+ return 0;
+ return extractEncodedIDBKey(p, end, &result->m_encodedUserKey);
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char>& encodedKey)
+ {
+ KeyPrefix prefix(databaseId, objectStoreId, kSpecialIndexNumber);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodedKey);
+ return ret;
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey)
+ {
+ return encode(databaseId, objectStoreId, encodeIDBKey(userKey));
+ }
+
+ int compare(const ExistsEntryKey& other)
+ {
+ return compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey);
+ }
+
+ PassRefPtr<IDBKey> userKey() const
+ {
+ RefPtr<IDBKey> key;
+ decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key);
+ return key;
+ }
+
+ static const int64_t kSpecialIndexNumber = kExistsEntryIndexId;
+
+private:
+ Vector<char> m_encodedUserKey;
+};
+
+class IndexDataKey {
+public:
+ IndexDataKey()
+ : m_databaseId(-1)
+ , m_objectStoreId(-1)
+ , m_indexId(-1)
+ , m_sequenceNumber(-1)
+ {
+ }
+
+ static const char* 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 >= kMinimumIndexId);
+ result->m_databaseId = prefix.m_databaseId;
+ result->m_objectStoreId = prefix.m_objectStoreId;
+ result->m_indexId = prefix.m_indexId;
+ p = extractEncodedIDBKey(p, limit, &result->m_encodedUserKey);
+ if (!p)
+ return 0;
+ if (p == limit) {
+ result->m_sequenceNumber = -1; // FIXME: We should change it so that all keys have a sequence number. Shouldn't need to handle this case.
+ return p;
+ }
+ return decodeVarInt(p, limit, result->m_sequenceNumber);
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector<char>& encodedUserKey, int64_t sequenceNumber)
+ {
+ KeyPrefix prefix(databaseId, objectStoreId, indexId);
+ Vector<char> ret = prefix.encode();
+ ret.append(encodedUserKey);
+ ret.append(encodeVarInt(sequenceNumber));
+ return ret;
+ }
+
+ static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey, int64_t sequenceNumber)
+ {
+ return encode(databaseId, objectStoreId, indexId, encodeIDBKey(userKey), sequenceNumber);
+ }
+
+ static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId)
+ {
+ return encode(databaseId, objectStoreId, INT32_MAX, maxIDBKey(), INT64_MAX);
+ }
+
+ int compare(const IndexDataKey& other, bool ignoreSequenceNumber)
+ {
+ ASSERT(m_databaseId >= 0);
+ ASSERT(m_objectStoreId >= 0);
+ ASSERT(m_indexId >= 0);
+ if (int x = compareEncodedIDBKeys(m_encodedUserKey, other.m_encodedUserKey))
+ return x;
+ if (ignoreSequenceNumber)
+ return 0;
+ return m_sequenceNumber - other.m_sequenceNumber;
+ }
+
+ int64_t databaseId() const
+ {
+ ASSERT(m_databaseId >= 0);
+ return m_databaseId;
+ }
+
+ int64_t objectStoreId() const
+ {
+ ASSERT(m_objectStoreId >= 0);
+ return m_objectStoreId;
+ }
+
+ int64_t indexId() const
+ {
+ ASSERT(m_indexId >= 0);
+ return m_indexId;
+ }
+
+ PassRefPtr<IDBKey> userKey() const
+ {
+ RefPtr<IDBKey> key;
+ decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key);
+ return key;
+ }
+
+private:
+ int64_t m_databaseId;
+ int64_t m_objectStoreId;
+ int64_t m_indexId;
+ Vector<char> m_encodedUserKey;
+ int64_t m_sequenceNumber;
+};
+
+namespace {
+template<typename KeyType>
+int decodeAndCompare(const LevelDBSlice& a, const LevelDBSlice& b)
+{
+ KeyType keyA;
+ KeyType keyB;
+
+ const char* ptrA = KeyType::decode(a.begin(), a.end(), &keyA);
+ ASSERT_UNUSED(ptrA, ptrA);
+ const char* ptrB = KeyType::decode(b.begin(), b.end(), &keyB);
+ ASSERT_UNUSED(ptrB, ptrB);
+
+ return keyA.compare(keyB);
+}
+}
+
+static int realCompare(const LevelDBSlice& a, const LevelDBSlice& b, bool indexKeys = false)
+{
+ 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 (int x = prefixA.compare(prefixB))
+ return x;
+
+ if (prefixA.type() == KeyPrefix::kGlobalMetaData) {
+ ASSERT(ptrA != endA);
+ ASSERT(ptrB != endB);
+
+ unsigned char typeByteA = *ptrA++;
+ unsigned char typeByteB = *ptrB++;
+
+ if (int x = typeByteA - typeByteB)
+ return x;
+
+ if (typeByteA <= 1)
+ return 0;
+ if (typeByteA == kDatabaseFreeListTypeByte)
+ return decodeAndCompare<DatabaseFreeListKey>(a, b);
+ if (typeByteA == kDatabaseNameTypeByte)
+ return decodeAndCompare<DatabaseNameKey>(a, b);
+ }
+
+ if (prefixA.type() == KeyPrefix::kDatabaseMetaData) {
+ ASSERT(ptrA != endA);
+ ASSERT(ptrB != endB);
+
+ unsigned char typeByteA = *ptrA++;
+ unsigned char typeByteB = *ptrB++;
+
+ if (int x = typeByteA - typeByteB)
+ return x;
+
+ if (typeByteA <= 3)
+ return 0;
+
+ if (typeByteA == kObjectStoreMetaDataTypeByte)
+ return decodeAndCompare<ObjectStoreMetaDataKey>(a, b);
+ if (typeByteA == kIndexMetaDataTypeByte)
+ return decodeAndCompare<IndexMetaDataKey>(a, b);
+ if (typeByteA == kObjectStoreFreeListTypeByte)
+ return decodeAndCompare<ObjectStoreFreeListKey>(a, b);
+ if (typeByteA == kIndexFreeListTypeByte)
+ return decodeAndCompare<IndexFreeListKey>(a, b);
+ if (typeByteA == kObjectStoreNamesTypeByte)
+ return decodeAndCompare<ObjectStoreNamesKey>(a, b);
+ if (typeByteA == kIndexNamesKeyTypeByte)
+ return decodeAndCompare<IndexNamesKey>(a, b);
+
+ return 0;
+ ASSERT_NOT_REACHED();
+ }
+
+ if (prefixA.type() == KeyPrefix::kObjectStoreData) {
+ 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.
+
+ return decodeAndCompare<ObjectStoreDataKey>(a, b);
+ }
+ if (prefixA.type() == KeyPrefix::kExistsEntry) {
+ 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.
+
+ return decodeAndCompare<ExistsEntryKey>(a, b);
+ }
+ if (prefixA.type() == KeyPrefix::kIndexData) {
+ 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.
+
+ IndexDataKey indexDataKeyA;
+ IndexDataKey indexDataKeyB;
+
+ ptrA = IndexDataKey::decode(a.begin(), endA, &indexDataKeyA);
+ ptrB = IndexDataKey::decode(b.begin(), endB, &indexDataKeyB);
+ ASSERT(ptrA);
+ ASSERT(ptrB);
+
+ bool ignoreSequenceNumber = indexKeys;
+ return indexDataKeyA.compare(indexDataKeyB, ignoreSequenceNumber);
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static int compareKeys(const LevelDBSlice& a, const LevelDBSlice& b)
+{
+ return realCompare(a, b);
+}
+
+static int compareIndexKeys(const LevelDBSlice& a, const LevelDBSlice& b)
+{
+ return realCompare(a, b, true);
+}
+
+class Comparator : public LevelDBComparator {
+public:
+ virtual int compare(const LevelDBSlice& a, const LevelDBSlice& b) const { return realCompare(a, b); }
+ virtual const char* name() const { return "idb_cmp1"; }
+};
+}
+
+static bool setUpMetadata(LevelDBDatabase* db)
+{
+ const Vector<char> metaDataKey = SchemaVersionKey::encode();
+
+ int64_t schemaVersion;
+ if (!getInt(db, metaDataKey, schemaVersion)) {
+ schemaVersion = 0;
+ if (!putInt(db, metaDataKey, schemaVersion))
+ return false;
+ }
+
+ // FIXME: Eventually, we'll need to be able to transition between schemas.
+ if (schemaVersion)
+ return false; // Don't know what to do with this version.
+
+ return true;
+}
+
+IDBLevelDBBackingStore::IDBLevelDBBackingStore(String identifier, IDBFactoryBackendImpl* factory, LevelDBDatabase* db)
+ : m_identifier(identifier)
+ , m_factory(factory)
+ , m_db(db)
+{
+ m_factory->addIDBBackingStore(identifier, this);
+}
+
+IDBLevelDBBackingStore::~IDBLevelDBBackingStore()
+{
+ m_factory->removeIDBBackingStore(m_identifier);
+}
+
+PassRefPtr<IDBBackingStore> IDBLevelDBBackingStore::open(SecurityOrigin* securityOrigin, const String& pathBaseArg, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory)
+{
+ String pathBase = pathBaseArg;
+
+ if (pathBase.isEmpty()) {
+ ASSERT_NOT_REACHED(); // FIXME: We need to handle this case for incognito and DumpRenderTree.
+ return 0;
+ }
+
+ if (!makeAllDirectories(pathBase)) {
+ LOG_ERROR("Unable to create IndexedDB database path %s", pathBase.utf8().data());
+ return 0;
+ }
+ // FIXME: We should eventually use the same LevelDB database for all origins.
+ String path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb.leveldb");
+
+ OwnPtr<LevelDBComparator> comparator(new Comparator());
+ LevelDBDatabase* db = LevelDBDatabase::open(path, comparator.get());
+ if (!db)
+ return 0;
+
+ // FIXME: Handle comparator name changes.
+
+ RefPtr<IDBLevelDBBackingStore> backingStore(adoptRef(new IDBLevelDBBackingStore(fileIdentifier, factory, db)));
+ backingStore->m_comparator = comparator.release();
+
+ if (!setUpMetadata(backingStore->m_db.get()))
+ return 0;
+
+ return backingStore.release();
+}
+
+bool IDBLevelDBBackingStore::extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId)
+{
+ const Vector<char> key = DatabaseNameKey::encode(m_identifier, name);
+
+ bool ok = getInt(m_db.get(), key, foundId);
+ if (!ok)
+ return false;
+
+ ok = getString(m_db.get(), DatabaseMetaDataKey::encode(foundId, DatabaseMetaDataKey::kUserVersion), foundVersion);
+ if (!ok)
+ return false;
+
+ return true;
+}
+
+static int64_t getNewDatabaseId(LevelDBDatabase* db)
+{
+ const Vector<char> freeListStartKey = DatabaseFreeListKey::encode(0);
+ const Vector<char> freeListStopKey = DatabaseFreeListKey::encode(INT64_MAX);
+
+ OwnPtr<LevelDBIterator> it(db->newIterator());
+ for (it->seek(freeListStartKey); it->isValid() && compareKeys(it->key(), freeListStopKey) < 0; it->next()) {
+ const char *p = it->key().begin();
+ const char *limit = it->key().end();
+
+ DatabaseFreeListKey freeListKey;
+ p = DatabaseFreeListKey::decode(p, limit, &freeListKey);
+ ASSERT(p);
+
+ bool ok = db->remove(it->key());
+ ASSERT_UNUSED(ok, ok);
+
+ return freeListKey.databaseId();
+ }
+
+ // If we got here, there was no free-list.
+ int64_t maxDatabaseId = -1;
+ if (!getInt(db, MaxDatabaseIdKey::encode(), maxDatabaseId))
+ maxDatabaseId = 0;
+
+ ASSERT(maxDatabaseId >= 0);
+
+ int64_t databaseId = maxDatabaseId + 1;
+ bool ok = putInt(db, MaxDatabaseIdKey::encode(), databaseId);
+ ASSERT_UNUSED(ok, ok);
+
+ return databaseId;
+
+}
+
+bool IDBLevelDBBackingStore::setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId)
+{
+ if (invalidRowId) {
+ rowId = getNewDatabaseId(m_db.get());
+
+ const Vector<char> key = DatabaseNameKey::encode(m_identifier, name);
+ if (!putInt(m_db.get(), key, rowId))
+ return false;
+ }
+
+ if (!putString(m_db.get(), DatabaseMetaDataKey::encode(rowId, DatabaseMetaDataKey::kUserVersion), version))
+ return false;
+
+ return true;
+}
+
+void IDBLevelDBBackingStore::getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags)
+{
+ const Vector<char> startKey = ObjectStoreMetaDataKey::encode(databaseId, 1, 0);
+ const Vector<char> stopKey = ObjectStoreMetaDataKey::encode(databaseId, INT64_MAX, 0);
+
+ OwnPtr<LevelDBIterator> it(m_db->newIterator());
+ 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();
+
+ ObjectStoreMetaDataKey metaDataKey;
+ p = ObjectStoreMetaDataKey::decode(p, limit, &metaDataKey);
+ ASSERT(p);
+
+ int64_t objectStoreId = metaDataKey.objectStoreId();
+
+ String objectStoreName = decodeString(it->value().begin(), it->value().end());
+
+ it->next();
+ if (!it->isValid()) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return;
+ }
+ String keyPath = decodeString(it->value().begin(), it->value().end());
+
+ it->next();
+ if (!it->isValid()) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return;
+ }
+ bool autoIncrement = *it->value().begin();
+
+ it->next(); // Is evicatble.
+ if (!it->isValid()) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return;
+ }
+
+ it->next(); // Last version.
+ if (!it->isValid()) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return;
+ }
+
+ it->next(); // Maxium index id allocated.
+ if (!it->isValid()) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return;
+ }
+
+ foundIds.append(objectStoreId);
+ foundNames.append(objectStoreName);
+ foundKeyPaths.append(keyPath);
+ foundAutoIncrementFlags.append(autoIncrement);
+ }
+}
+
+static int64_t getNewObjectStoreId(LevelDBDatabase* db, int64_t databaseId)
+{
+ const Vector<char> freeListStartKey = ObjectStoreFreeListKey::encode(databaseId, 0);
+ const Vector<char> freeListStopKey = ObjectStoreFreeListKey::encode(databaseId, INT64_MAX);
+
+ OwnPtr<LevelDBIterator> it(db->newIterator());
+ for (it->seek(freeListStartKey); it->isValid() && compareKeys(it->key(), freeListStopKey) < 0; it->next()) {
+ const char* p = it->key().begin();
+ const char* limit = it->key().end();
+
+ ObjectStoreFreeListKey freeListKey;
+ p = ObjectStoreFreeListKey::decode(p, limit, &freeListKey);
+ ASSERT(p);
+
+ bool ok = db->remove(it->key());
+ ASSERT_UNUSED(ok, ok);
+
+ return freeListKey.objectStoreId();
+ }
+
+ int64_t maxObjectStoreId;
+ const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::kMaxObjectStoreId);
+ if (!getInt(db, maxObjectStoreIdKey, maxObjectStoreId))
+ maxObjectStoreId = 0;
+
+ int64_t objectStoreId = maxObjectStoreId + 1;
+ bool ok = putInt(db, maxObjectStoreIdKey, objectStoreId);
+ ASSERT_UNUSED(ok, ok);
+
+ return objectStoreId;
+}
+
+bool IDBLevelDBBackingStore::createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId)
+{
+ int64_t objectStoreId = getNewObjectStoreId(m_db.get(), databaseId);
+
+ const Vector<char> nameKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0);
+ const Vector<char> keyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 1);
+ const Vector<char> autoIncrementKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 2);
+ const Vector<char> evictableKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 3);
+ const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 4);
+ const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 5);
+ const Vector<char> namesKey = ObjectStoreNamesKey::encode(databaseId, name);
+
+ bool ok = putString(m_db.get(), nameKey, name);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putString(m_db.get(), keyPathKey, keyPath);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putInt(m_db.get(), autoIncrementKey, autoIncrement);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putInt(m_db.get(), evictableKey, false);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putInt(m_db.get(), lastVersionKey, 1);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putInt(m_db.get(), maxIndexIdKey, kMinimumIndexId);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putInt(m_db.get(), namesKey, objectStoreId);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ assignedObjectStoreId = objectStoreId;
+
+ return true;
+}
+
+static bool deleteRange(LevelDBDatabase* db, const Vector<char>& begin, const Vector<char>& end)
+{
+ // FIXME: LevelDB may be able to provide a bulk operation that we can do first.
+ OwnPtr<LevelDBIterator> it(db->newIterator());
+ for (it->seek(begin); it->isValid() && compareKeys(it->key(), end) < 0; it->next()) {
+ if (!db->remove(it->key()))
+ return false;
+ }
+
+ return true;
+}
+
+void IDBLevelDBBackingStore::deleteObjectStore(int64_t databaseId, int64_t objectStoreId)
+{
+ String objectStoreName;
+ getString(m_db.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), objectStoreName);
+
+ if (!deleteRange(m_db.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 6)))
+ return; // FIXME: Report error.
+
+ putString(m_db.get(), ObjectStoreFreeListKey::encode(databaseId, objectStoreId), "");
+ m_db->remove(ObjectStoreNamesKey::encode(databaseId, objectStoreName));
+
+ if (!deleteRange(m_db.get(), IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encode(databaseId, objectStoreId, INT64_MAX)))
+ return; // FIXME: Report error.
+ if (!deleteRange(m_db.get(), IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encode(databaseId, objectStoreId, INT64_MAX, 0)))
+ return; // FIXME: Report error.
+
+ clearObjectStore(databaseId, objectStoreId);
+}
+
+String IDBLevelDBBackingStore::getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey& key)
+{
+ const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
+ Vector<char> data;
+
+ if (!m_db->get(leveldbKey, data))
+ return String();
+
+ int64_t version;
+ const char* p = decodeVarInt(data.begin(), data.end(), version);
+ if (!p)
+ return String();
+ (void) version;
+
+ return decodeString(p, data.end());
+}
+
+namespace {
+class LevelDBRecordIdentifier : public IDBBackingStore::ObjectStoreRecordIdentifier {
+public:
+ static PassRefPtr<LevelDBRecordIdentifier> create(const Vector<char>& primaryKey, int64_t version) { return adoptRef(new LevelDBRecordIdentifier(primaryKey, version)); }
+ static PassRefPtr<LevelDBRecordIdentifier> create() { return adoptRef(new LevelDBRecordIdentifier()); }
+
+ virtual bool isValid() const { return m_primaryKey.isEmpty(); }
+ Vector<char> primaryKey() const { return m_primaryKey; }
+ void setPrimaryKey(const Vector<char>& primaryKey) { m_primaryKey = primaryKey; }
+ int64_t version() const { return m_version; }
+ void setVersion(int64_t version) { m_version = version; }
+
+private:
+ LevelDBRecordIdentifier(const Vector<char>& primaryKey, int64_t version) : m_primaryKey(primaryKey), m_version(version) { ASSERT(!primaryKey.isEmpty()); }
+ LevelDBRecordIdentifier() : m_primaryKey(), m_version(-1) {}
+
+ Vector<char> m_primaryKey; // FIXME: Make it more clear that this is the *encoded* version of the key.
+ int64_t m_version;
+};
+}
+
+static int64_t getNewVersionNumber(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId)
+{
+ const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 4);
+
+ int64_t lastVersion = -1;
+ if (!getInt(db, lastVersionKey, lastVersion))
+ lastVersion = 0;
+
+ ASSERT(lastVersion >= 0);
+
+ int64_t version = lastVersion + 1;
+ bool ok = putInt(db, lastVersionKey, version);
+ ASSERT_UNUSED(ok, ok);
+
+ ASSERT(version > lastVersion); // FIXME: Think about how we want to handle the overflow scenario.
+
+ return version;
+}
+
+bool IDBLevelDBBackingStore::putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey& key, const String& value, ObjectStoreRecordIdentifier* recordIdentifier)
+{
+ int64_t version = getNewVersionNumber(m_db.get(), databaseId, objectStoreId);
+ const Vector<char> objectStoredataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
+
+ Vector<char> v;
+ v.append(encodeVarInt(version));
+ v.append(encodeString(value));
+
+ if (!m_db->put(objectStoredataKey, v))
+ return false;
+
+ const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, key);
+ if (!m_db->put(existsEntryKey, encodeInt(version)))
+ return false;
+
+ LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<LevelDBRecordIdentifier*>(recordIdentifier);
+ levelDBRecordIdentifier->setPrimaryKey(encodeIDBKey(key));
+ levelDBRecordIdentifier->setVersion(version);
+ return true;
+}
+
+void IDBLevelDBBackingStore::clearObjectStore(int64_t databaseId, int64_t objectStoreId)
+{
+ const Vector<char> startKey = KeyPrefix(databaseId, objectStoreId, 0).encode();
+ const Vector<char> stopKey = KeyPrefix(databaseId, objectStoreId + 1, 0).encode();
+
+ deleteRange(m_db.get(), startKey, stopKey);
+}
+
+PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> IDBLevelDBBackingStore::createInvalidRecordIdentifier()
+{
+ return LevelDBRecordIdentifier::create();
+}
+
+void IDBLevelDBBackingStore::deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier* recordIdentifier)
+{
+ const LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<const LevelDBRecordIdentifier*>(recordIdentifier);
+ const Vector<char> key = ObjectStoreDataKey::encode(databaseId, objectStoreId, levelDBRecordIdentifier->primaryKey());
+ m_db->remove(key);
+}
+
+double IDBLevelDBBackingStore::nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId)
+{
+ const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
+ const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
+
+ OwnPtr<LevelDBIterator> it(m_db->newIterator());
+
+ int maxNumericKey = 0;
+
+ // FIXME: Be more efficient: seek to something after the object store data, then reverse.
+
+ 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;
+ }
+ }
+
+ return maxNumericKey + 1;
+}
+
+bool IDBLevelDBBackingStore::keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey& key, ObjectStoreRecordIdentifier* foundRecordIdentifier)
+{
+ const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
+ Vector<char> data;
+
+ if (!m_db->get(leveldbKey, data))
+ return false;
+
+ int64_t version;
+ if (!decodeVarInt(data.begin(), data.end(), version))
+ return false;
+
+ LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<LevelDBRecordIdentifier*>(foundRecordIdentifier);
+ levelDBRecordIdentifier->setPrimaryKey(encodeIDBKey(key));
+ levelDBRecordIdentifier->setVersion(version);
+ return true;
+}
+
+bool IDBLevelDBBackingStore::forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback& callback)
+{
+ const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
+ const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
+
+ OwnPtr<LevelDBIterator> it(m_db->newIterator());
+ 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);
+
+ RefPtr<IDBKey> primaryKey = dataKey.userKey();
+
+ int64_t version;
+ const char* q = decodeVarInt(it->value().begin(), it->value().end(), version);
+ if (!q)
+ return false;
+
+ RefPtr<LevelDBRecordIdentifier> ri = LevelDBRecordIdentifier::create(encodeIDBKey(*primaryKey), version);
+ String idbValue = decodeString(q, it->value().end());
+
+ callback.callback(ri.get(), idbValue);
+ }
+
+ return true;
+}
+
+void IDBLevelDBBackingStore::getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags)
+{
+ const Vector<char> startKey = IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0);
+ const Vector<char> stopKey = IndexMetaDataKey::encode(databaseId, objectStoreId + 1, 0, 0);
+
+ OwnPtr<LevelDBIterator> it(m_db->newIterator());
+ 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();
+
+ IndexMetaDataKey metaDataKey;
+ p = IndexMetaDataKey::decode(p, limit, &metaDataKey);
+ ASSERT(p);
+
+ int64_t indexId = metaDataKey.indexId();
+ ASSERT(!metaDataKey.metaDataType());
+
+ String indexName = decodeString(it->value().begin(), it->value().end());
+ it->next();
+ if (!it->isValid()) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return;
+ }
+
+ bool indexUnique = *it->value().begin();
+ it->next();
+ if (!it->isValid()) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return;
+ }
+
+ String keyPath = decodeString(it->value().begin(), it->value().end());
+
+ foundIds.append(indexId);
+ foundNames.append(indexName);
+ foundKeyPaths.append(keyPath);
+ foundUniqueFlags.append(indexUnique);
+ }
+}
+
+static int64_t getNewIndexId(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId)
+{
+ const Vector<char> startKey = IndexFreeListKey::encode(databaseId, objectStoreId, 0);
+ const Vector<char> stopKey = IndexFreeListKey::encode(databaseId, objectStoreId, INT64_MAX);
+
+ OwnPtr<LevelDBIterator> it(db->newIterator());
+ 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();
+
+ IndexFreeListKey freeListKey;
+ p = IndexFreeListKey::decode(p, limit, &freeListKey);
+ ASSERT(p);
+
+ bool ok = db->remove(it->key());
+ ASSERT_UNUSED(ok, ok);
+
+ ASSERT(freeListKey.indexId() >= kMinimumIndexId);
+ return freeListKey.indexId();
+ }
+
+ int64_t maxIndexId;
+ const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 5);
+ if (!getInt(db, maxIndexIdKey, maxIndexId))
+ maxIndexId = kMinimumIndexId;
+
+ int64_t indexId = maxIndexId + 1;
+ bool ok = putInt(db, maxIndexIdKey, indexId);
+ if (!ok)
+ return false;
+
+ return indexId;
+}
+
+bool IDBLevelDBBackingStore::createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId)
+{
+ indexId = getNewIndexId(m_db.get(), databaseId, objectStoreId);
+
+ const Vector<char> nameKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0);
+ const Vector<char> uniqueKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 1);
+ const Vector<char> keyPathKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 2);
+
+ bool ok = putString(m_db.get(), nameKey, name);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putInt(m_db.get(), uniqueKey, isUnique);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ ok = putString(m_db.get(), keyPathKey, keyPath);
+ if (!ok) {
+ LOG_ERROR("Internal Indexed DB error.");
+ return false;
+ }
+
+ return true;
+}
+
+void IDBLevelDBBackingStore::deleteIndex(int64_t, int64_t, int64_t)
+{
+ ASSERT_NOT_REACHED(); // FIXME: Implement and add layout test.
+ return;
+}
+
+bool IDBLevelDBBackingStore::putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, const ObjectStoreRecordIdentifier* recordIdentifier)
+{
+ ASSERT(indexId >= kMinimumIndexId);
+ const LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<const LevelDBRecordIdentifier*>(recordIdentifier);
+
+ const int64_t globalSequenceNumber = getNewVersionNumber(m_db.get(), databaseId, objectStoreId);
+ const Vector<char> indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, globalSequenceNumber);
+
+ Vector<char> data;
+ data.append(encodeVarInt(levelDBRecordIdentifier->version()));
+ data.append(levelDBRecordIdentifier->primaryKey());
+
+ return m_db->put(indexDataKey, data);
+}
+
+static bool findGreatestKeyLessThan(LevelDBDatabase* db, const Vector<char>& target, Vector<char>& foundKey)
+{
+ OwnPtr<LevelDBIterator> it(db->newIterator());
+ it->seek(target);
+
+ if (!it->isValid()) {
+ it->seekToLast();
+ if (!it->isValid())
+ return false;
+ }
+
+ while (compareIndexKeys(it->key(), target) >= 0) {
+ it->prev();
+ if (!it->isValid())
+ return false;
+ }
+
+ foundKey.clear();
+ foundKey.append(it->key().begin(), it->key().end() - it->key().begin());
+ return true;
+}
+
+bool IDBLevelDBBackingStore::deleteIndexDataForRecord(int64_t, int64_t, int64_t, const ObjectStoreRecordIdentifier*)
+{
+ // FIXME: This isn't needed since we invalidate index data via the version number mechanism.
+ return true;
+}
+
+String IDBLevelDBBackingStore::getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
+{
+ RefPtr<IDBKey> primaryKey = getPrimaryKeyViaIndex(databaseId, objectStoreId, indexId, key);
+ if (!primaryKey)
+ return String();
+
+ return getObjectStoreRecord(databaseId, objectStoreId, *primaryKey);
+}
+
+static bool versionExists(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId, int64_t version, const Vector<char>& encodedPrimaryKey)
+{
+ const Vector<char> key = ExistsEntryKey::encode(databaseId, objectStoreId, encodedPrimaryKey);
+ Vector<char> data;
+
+ if (!db->get(key, data))
+ return false;
+
+ return decodeInt(data.begin(), data.end()) == version;
+}
+
+PassRefPtr<IDBKey> IDBLevelDBBackingStore::getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
+{
+ const Vector<char> leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0);
+ OwnPtr<LevelDBIterator> it(m_db->newIterator());
+ it->seek(leveldbKey);
+
+ for (;;) {
+ if (!it->isValid())
+ return 0;
+ if (compareIndexKeys(it->key(), leveldbKey) > 0)
+ return 0;
+
+ int64_t version;
+ const char* p = decodeVarInt(it->value().begin(), it->value().end(), version);
+ if (!p)
+ return 0;
+ Vector<char> encodedPrimaryKey;
+ encodedPrimaryKey.append(p, it->value().end() - p);
+
+ if (!versionExists(m_db.get(), databaseId, objectStoreId, version, encodedPrimaryKey)) {
+ // Delete stale index data entry and continue.
+ m_db->remove(it->key());
+ it->next();
+ continue;
+ }
+
+ RefPtr<IDBKey> primaryKey;
+ decodeIDBKey(encodedPrimaryKey.begin(), encodedPrimaryKey.end(), primaryKey);
+ return primaryKey.release();
+ }
+}
+
+bool IDBLevelDBBackingStore::keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
+{
+ const Vector<char> levelDBKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0);
+ OwnPtr<LevelDBIterator> it(m_db->newIterator());
+
+ bool found = false;
+
+ it->seek(levelDBKey);
+ if (it->isValid() && !compareIndexKeys(it->key(), levelDBKey))
+ found = true;
+
+ return found;
+}
+
+namespace {
+class CursorImplCommon : public IDBBackingStore::Cursor {
+public:
+ // IDBBackingStore::Cursor
+ virtual bool continueFunction(const IDBKey*);
+ virtual PassRefPtr<IDBKey> key() { return m_currentKey; }
+ virtual PassRefPtr<IDBKey> primaryKey() { return m_currentKey; }
+ virtual String value() = 0;
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() = 0; // FIXME: I don't think this is actually used, so drop it.
+ virtual int64_t indexDataId() = 0;
+
+ virtual bool loadCurrentRow() = 0;
+ bool firstSeek();
+
+protected:
+ CursorImplCommon(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : m_db(db)
+ , m_lowKey(lowKey)
+ , m_lowOpen(lowOpen)
+ , m_highKey(highKey)
+ , m_highOpen(highOpen)
+ , m_forward(forward)
+ {
+ }
+ virtual ~CursorImplCommon() {}
+
+ LevelDBDatabase* m_db;
+ OwnPtr<LevelDBIterator> m_iterator;
+ Vector<char> m_lowKey;
+ bool m_lowOpen;
+ Vector<char> m_highKey;
+ bool m_highOpen;
+ bool m_forward;
+ RefPtr<IDBKey> m_currentKey;
+};
+
+bool CursorImplCommon::firstSeek()
+{
+ m_iterator = m_db->newIterator();
+
+ if (m_forward)
+ m_iterator->seek(m_lowKey);
+ else
+ m_iterator->seek(m_highKey);
+
+ for (;;) {
+ if (!m_iterator->isValid())
+ return false;
+
+ if (m_forward && m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) >= 0)
+ return false;
+ if (m_forward && !m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) > 0)
+ return false;
+ if (!m_forward && m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) <= 0)
+ return false;
+ if (!m_forward && !m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) < 0)
+ return false;
+
+ if (m_forward && m_lowOpen) {
+ // lowKey not included in the range.
+ if (compareIndexKeys(m_iterator->key(), m_lowKey) <= 0) {
+ m_iterator->next();
+ continue;
+ }
+ }
+ if (!m_forward && m_highOpen) {
+ // highKey not included in the range.
+ if (compareIndexKeys(m_iterator->key(), m_highKey) >= 0) {
+ m_iterator->prev();
+ continue;
+ }
+ }
+
+ if (!loadCurrentRow()) {
+ if (m_forward)
+ m_iterator->next();
+ else
+ m_iterator->prev();
+ continue;
+ }
+
+ return true;
+ }
+}
+
+bool CursorImplCommon::continueFunction(const IDBKey* key)
+{
+ // FIXME: This shares a lot of code with firstSeek.
+
+ for (;;) {
+ if (m_forward)
+ m_iterator->next();
+ else
+ m_iterator->prev();
+
+ if (!m_iterator->isValid())
+ return false;
+
+ Vector<char> trash;
+ if (!m_db->get(m_iterator->key(), trash))
+ continue;
+
+ if (m_forward && m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) >= 0) // high key not included in range
+ return false;
+ if (m_forward && !m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) > 0)
+ return false;
+ if (!m_forward && m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) <= 0) // low key not included in range
+ return false;
+ if (!m_forward && !m_lowOpen && compareIndexKeys(m_iterator->key(), m_lowKey) < 0)
+ return false;
+
+ if (!loadCurrentRow())
+ continue;
+
+ if (key) {
+ if (m_forward) {
+ if (m_currentKey->isLessThan(key))
+ continue;
+ } else {
+ if (key->isLessThan(m_currentKey.get()))
+ continue;
+ }
+ }
+
+ // FIXME: Obey the uniqueness constraint (and test for it!)
+
+ break;
+ }
+
+ return true;
+}
+
+class ObjectStoreCursorImpl : public CursorImplCommon {
+public:
+ static PassRefPtr<ObjectStoreCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ {
+ return adoptRef(new ObjectStoreCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward));
+ }
+
+ // CursorImplCommon
+ virtual String value() { return m_currentValue; }
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; }
+ virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; }
+ virtual bool loadCurrentRow();
+
+private:
+ ObjectStoreCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward)
+ {
+ }
+
+ String m_currentValue;
+};
+
+bool ObjectStoreCursorImpl::loadCurrentRow()
+{
+ const char* p = m_iterator->key().begin();
+ const char* keyLimit = m_iterator->key().end();
+
+ ObjectStoreDataKey objectStoreDataKey;
+ p = ObjectStoreDataKey::decode(p, keyLimit, &objectStoreDataKey);
+ ASSERT(p);
+ if (!p)
+ return false;
+
+ m_currentKey = objectStoreDataKey.userKey();
+
+ int64_t version;
+ const char* q = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), version);
+ ASSERT(q);
+ if (!q)
+ return false;
+ (void) version;
+
+ m_currentValue = decodeString(q, m_iterator->value().end());
+
+ return true;
+}
+
+class IndexKeyCursorImpl : public CursorImplCommon {
+public:
+ static PassRefPtr<IndexKeyCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ {
+ return adoptRef(new IndexKeyCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward));
+ }
+
+ // CursorImplCommon
+ virtual String value() { ASSERT_NOT_REACHED(); return String(); }
+ virtual PassRefPtr<IDBKey> primaryKey() { return m_primaryKey; }
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; }
+ virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; }
+ virtual bool loadCurrentRow();
+
+private:
+ IndexKeyCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward)
+ {
+ }
+
+ RefPtr<IDBKey> m_primaryKey;
+};
+
+bool IndexKeyCursorImpl::loadCurrentRow()
+{
+ const char* p = m_iterator->key().begin();
+ const char* keyLimit = m_iterator->key().end();
+ IndexDataKey indexDataKey;
+ p = IndexDataKey::decode(p, keyLimit, &indexDataKey);
+
+ m_currentKey = indexDataKey.userKey();
+
+ int64_t indexDataVersion;
+ const char* q = decodeVarInt(m_iterator->value().begin(), m_iterator->value().end(), indexDataVersion);
+ ASSERT(q);
+ if (!q)
+ return false;
+
+ q = decodeIDBKey(q, m_iterator->value().end(), m_primaryKey);
+ ASSERT(q);
+ if (!q)
+ return false;
+
+ Vector<char> primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey);
+
+ Vector<char> result;
+ if (!m_db->get(primaryLevelDBKey, result))
+ return false;
+
+ int64_t objectStoreDataVersion;
+ const char* t = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion);
+ ASSERT(t);
+ if (!t)
+ return false;
+
+ if (objectStoreDataVersion != indexDataVersion) { // FIXME: This is probably not very well covered by the layout tests.
+ m_db->remove(m_iterator->key());
+ return false;
+ }
+
+ return true;
+}
+
+class IndexCursorImpl : public CursorImplCommon {
+public:
+ static PassRefPtr<IndexCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ {
+ return adoptRef(new IndexCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward));
+ }
+
+ // CursorImplCommon
+ virtual String value() { return m_value; }
+ virtual PassRefPtr<IDBKey> primaryKey() { return m_primaryKey; }
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; }
+ virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; }
+ bool loadCurrentRow();
+
+private:
+ IndexCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward)
+ {
+ }
+
+ RefPtr<IDBKey> m_primaryKey;
+ String m_value;
+ Vector<char> m_primaryLevelDBKey;
+};
+
+bool IndexCursorImpl::loadCurrentRow()
+{
+ const char *p = m_iterator->key().begin();
+ const char *limit = m_iterator->key().end();
+
+ IndexDataKey indexDataKey;
+ p = IndexDataKey::decode(p, limit, &indexDataKey);
+
+ m_currentKey = indexDataKey.userKey();
+
+ const char *q = m_iterator->value().begin();
+ const char *valueLimit = m_iterator->value().end();
+
+ int64_t indexDataVersion;
+ q = decodeVarInt(q, valueLimit, indexDataVersion);
+ ASSERT(q);
+ if (!q)
+ return false;
+ q = decodeIDBKey(q, valueLimit, m_primaryKey);
+ ASSERT(q);
+ if (!q)
+ return false;
+
+ m_primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey);
+
+ Vector<char> result;
+ if (!m_db->get(m_primaryLevelDBKey, result))
+ return false;
+
+ int64_t objectStoreDataVersion;
+ const char* t = decodeVarInt(result.begin(), result.end(), objectStoreDataVersion);
+ ASSERT(t);
+ if (!t)
+ return false;
+
+ if (objectStoreDataVersion != indexDataVersion) {
+ m_db->remove(m_iterator->key());
+ return false;
+ }
+
+ m_value = decodeString(t, result.end());
+ return true;
+}
+
+}
+
+static bool findLastIndexKeyEqualTo(LevelDBDatabase* db, const Vector<char>& target, Vector<char>& foundKey)
+{
+ OwnPtr<LevelDBIterator> it(db->newIterator());
+ it->seek(target);
+
+ if (!it->isValid())
+ return false;
+
+ while (it->isValid() && !compareIndexKeys(it->key(), target)) {
+ foundKey.clear();
+ foundKey.append(it->key().begin(), it->key().end() - it->key().begin());
+ it->next();
+ }
+
+ return true;
+}
+
+PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+ bool lowerBound = range && range->lower();
+ bool upperBound = range && range->upper();
+ bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT);
+
+ bool lowerOpen, upperOpen;
+ Vector<char> startKey, stopKey;
+
+ if (!lowerBound) {
+ startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
+ lowerOpen = true; // Not included.
+ } else {
+ startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->lower());
+ lowerOpen = range->lowerOpen();
+ }
+
+ if (!upperBound) {
+ stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
+ upperOpen = true; // Not included.
+
+ if (!forward) { // We need a key that exists.
+ if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey))
+ return 0;
+ upperOpen = false;
+ }
+ } else {
+ stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, *range->upper());
+ upperOpen = range->upperOpen();
+ }
+
+ RefPtr<ObjectStoreCursorImpl> cursor = ObjectStoreCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
+ if (!cursor->firstSeek())
+ return 0;
+
+ return cursor.release();
+}
+
+PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+ bool lowerBound = range && range->lower();
+ bool upperBound = range && range->upper();
+ bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT);
+
+ bool lowerOpen, upperOpen;
+ Vector<char> startKey, stopKey;
+
+ if (!lowerBound) {
+ startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, minIDBKey(), 0);
+ lowerOpen = false; // Included.
+ } else {
+ startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->lower(), 0);
+ lowerOpen = range->lowerOpen();
+ }
+
+ if (!upperBound) {
+ stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, maxIDBKey(), 0);
+ upperOpen = false; // Included.
+
+ if (!forward) { // We need a key that exists.
+ if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey))
+ return 0;
+ upperOpen = false;
+ }
+ } else {
+ stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper(), 0);
+ if (!findLastIndexKeyEqualTo(m_db.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys.
+ return 0;
+ upperOpen = range->upperOpen();
+ }
+
+ RefPtr<IndexKeyCursorImpl> cursor = IndexKeyCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
+ if (!cursor->firstSeek())
+ return 0;
+
+ return cursor.release();
+}
+
+PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+ bool lowerBound = range && range->lower();
+ bool upperBound = range && range->upper();
+ bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT);
+
+ bool lowerOpen, upperOpen;
+ Vector<char> startKey, stopKey;
+
+ if (!lowerBound) {
+ startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, minIDBKey(), 0);
+ lowerOpen = false; // Included.
+ } else {
+ startKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->lower(), 0);
+ lowerOpen = range->lowerOpen();
+ }
+
+ if (!upperBound) {
+ stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, maxIDBKey(), 0);
+ upperOpen = false; // Included.
+
+ if (!forward) { // We need a key that exists.
+ if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey))
+ return 0;
+ upperOpen = false;
+ }
+ } else {
+ stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper(), 0);
+ if (!findLastIndexKeyEqualTo(m_db.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys.
+ return 0;
+ upperOpen = range->upperOpen();
+ }
+
+ RefPtr<IndexCursorImpl> cursor = IndexCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
+ if (!cursor->firstSeek())
+ return 0;
+
+ return cursor.release();
+}
+
+namespace {
+class DummyTransaction : public IDBBackingStore::Transaction {
+public:
+ virtual void begin() {}
+ virtual void commit() {}
+ virtual void rollback() {}
+};
+}
+
+PassRefPtr<IDBBackingStore::Transaction> IDBLevelDBBackingStore::createTransaction()
+{
+ // FIXME: We need to implement a transaction abstraction that allows for roll-backs, and write tests for it.
+ return adoptRef(new DummyTransaction());
+}
+
+// FIXME: deleteDatabase should be part of IDBBackingStore.
+
+} // namespace WebCore
+
+#endif // ENABLE(LEVELDB)
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/storage/IDBLevelDBBackingStore.h b/Source/WebCore/storage/IDBLevelDBBackingStore.h
new file mode 100644
index 0000000..a8647da
--- /dev/null
+++ b/Source/WebCore/storage/IDBLevelDBBackingStore.h
@@ -0,0 +1,91 @@
+/*
+ * 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 IDBLevelDBBackingStore_h
+#define IDBLevelDBBackingStore_h
+
+#if ENABLE(INDEXED_DATABASE)
+#if ENABLE(LEVELDB)
+
+#include "IDBBackingStore.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class LevelDBComparator;
+class LevelDBDatabase;
+
+class IDBLevelDBBackingStore : public IDBBackingStore {
+public:
+ static PassRefPtr<IDBBackingStore> open(SecurityOrigin*, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl*);
+ virtual ~IDBLevelDBBackingStore();
+
+ virtual bool extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId);
+ virtual bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId);
+
+ virtual void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags);
+ virtual bool createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId);
+ virtual void deleteObjectStore(int64_t databaseId, int64_t objectStoreId);
+ virtual PassRefPtr<ObjectStoreRecordIdentifier> createInvalidRecordIdentifier();
+ virtual String getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&);
+ virtual bool putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&, const String& value, ObjectStoreRecordIdentifier*);
+ virtual void clearObjectStore(int64_t databaseId, int64_t objectStoreId);
+ virtual void deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier*);
+ virtual double nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId);
+ virtual bool keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey&, ObjectStoreRecordIdentifier* foundRecordIdentifier);
+
+ virtual bool forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback&);
+
+ virtual void getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags);
+ virtual bool createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId);
+ virtual void deleteIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
+ virtual bool putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const ObjectStoreRecordIdentifier*);
+ virtual bool deleteIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const ObjectStoreRecordIdentifier*);
+ virtual String getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&);
+ virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&);
+ virtual bool keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&);
+
+ virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction);
+ virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
+ virtual PassRefPtr<Cursor> openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
+
+ virtual PassRefPtr<Transaction> createTransaction();
+
+private:
+ IDBLevelDBBackingStore(String identifier, IDBFactoryBackendImpl*, LevelDBDatabase*);
+
+ String m_identifier;
+ RefPtr<IDBFactoryBackendImpl> m_factory;
+ OwnPtr<LevelDBDatabase> m_db;
+ OwnPtr<LevelDBComparator> m_comparator;
+};
+
+} // namespace WebCore
+
+
+#endif // ENABLE(LEVELDB)
+#endif // ENABLE(INDEXED_DATABASE)
+
+#endif // IDBLevelDBBackingStore_h
diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp
index 0433ed7..cb6b0da 100644
--- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -50,8 +50,9 @@ IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl()
{
}
-IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
+IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
: m_backingStore(backingStore)
+ , m_databaseId(databaseId)
, m_id(id)
, m_name(name)
, m_keyPath(keyPath)
@@ -61,8 +62,9 @@ IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingSto
loadIndexes();
}
-IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement)
+IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement)
: m_backingStore(backingStore)
+ , m_databaseId(databaseId)
, m_id(InvalidId)
, m_name(name)
, m_keyPath(keyPath)
@@ -90,7 +92,7 @@ void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCal
void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
{
- String wireData = objectStore->m_backingStore->getObjectStoreRecord(objectStore->id(), *key);
+ String wireData = objectStore->m_backingStore->getObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), *key);
if (wireData.isNull()) {
callbacks->onSuccess(SerializedScriptValue::undefinedValue());
return;
@@ -227,8 +229,8 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
indexKeys.append(key.release());
}
- int64_t dataRowId = InvalidId;
- bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, dataRowId);
+ RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier = objectStore->m_backingStore->createInvalidRecordIdentifier();
+ bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->m_databaseId, objectStore->id(), *key, recordIdentifier.get());
if (putMode == AddOnly && isExistingValue) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store."));
@@ -237,14 +239,7 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
// Before this point, don't do any mutation. After this point, rollback the transaction in case of error.
- if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->id(), *key, value->toWireString(), dataRowId, dataRowId == InvalidId)) {
- // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
- transaction->abort();
- return;
- }
-
- if (!objectStore->m_backingStore->deleteIndexDataForRecord(dataRowId)) {
+ if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), *key, value->toWireString(), recordIdentifier.get())) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
@@ -255,7 +250,15 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) {
if (!it->second->hasValidId())
continue; // The index object has been created, but does not exist in the database yet.
- if (!objectStore->m_backingStore->putIndexDataForRecord(it->second->id(), *indexKeys[i], dataRowId)) {
+
+ if (!objectStore->m_backingStore->deleteIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), it->second->id(), recordIdentifier.get())) {
+ // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
+ callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
+ transaction->abort();
+ return;
+ }
+
+ if (!objectStore->m_backingStore->putIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), it->second->id(), *indexKeys[i], recordIdentifier.get())) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
@@ -283,13 +286,21 @@ void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKey> prpKey, PassRe
void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
{
- int64_t id;
- if (!objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, id)) {
+ RefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> recordIdentifier = objectStore->m_backingStore->createInvalidRecordIdentifier();
+ if (!objectStore->m_backingStore->keyExistsInObjectStore(objectStore->m_databaseId, objectStore->id(), *key, recordIdentifier.get())) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store."));
return;
}
- objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->id(), id);
+ for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it) {
+ if (!it->second->hasValidId())
+ continue; // The index object has been created, but does not exist in the database yet.
+
+ if (!objectStore->m_backingStore->deleteIndexDataForRecord(objectStore->m_databaseId, objectStore->id(), it->second->id(), recordIdentifier.get()))
+ ASSERT_NOT_REACHED();
+ }
+
+ objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->m_databaseId, objectStore->id(), recordIdentifier.get());
callbacks->onSuccess(SerializedScriptValue::nullValue());
}
@@ -309,26 +320,28 @@ void IDBObjectStoreBackendImpl::clear(PassRefPtr<IDBCallbacks> prpCallbacks, IDB
void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks)
{
- objectStore->m_backingStore->clearObjectStore(objectStore->id());
+ objectStore->m_backingStore->clearObjectStore(objectStore->m_databaseId, objectStore->id());
callbacks->onSuccess(SerializedScriptValue::undefinedValue());
}
namespace {
class PopulateIndexCallback : public IDBBackingStore::ObjectStoreRecordCallback {
public:
- PopulateIndexCallback(IDBBackingStore& backingStore, const String& indexKeyPath, int64_t indexId)
+ PopulateIndexCallback(IDBBackingStore& backingStore, const String& indexKeyPath, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
: m_backingStore(backingStore)
, m_indexKeyPath(indexKeyPath)
+ , m_databaseId(databaseId)
+ , m_objectStoreId(objectStoreId)
, m_indexId(indexId)
{
}
- virtual bool callback(int64_t objectStoreDataId, const String& value)
+ virtual bool callback(const IDBBackingStore::ObjectStoreRecordIdentifier* recordIdentifier, const String& value)
{
RefPtr<SerializedScriptValue> objectValue = SerializedScriptValue::createFromWire(value);
RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(objectValue.get(), m_indexKeyPath);
- if (!m_backingStore.putIndexDataForRecord(m_indexId, *indexKey, objectStoreDataId))
+ if (!m_backingStore.putIndexDataForRecord(m_databaseId, m_objectStoreId, m_indexId, *indexKey, recordIdentifier))
return false;
return true;
@@ -337,14 +350,16 @@ public:
private:
IDBBackingStore& m_backingStore;
const String& m_indexKeyPath;
+ int64_t m_databaseId;
+ int64_t m_objectStoreId;
int64_t m_indexId;
};
}
-static bool populateIndex(IDBBackingStore& backingStore, int64_t objectStoreId, int64_t indexId, const String& indexKeyPath)
+static bool populateIndex(IDBBackingStore& backingStore, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& indexKeyPath)
{
- PopulateIndexCallback callback(backingStore, indexKeyPath, indexId);
- if (!backingStore.forEachObjectStoreRecord(objectStoreId, callback))
+ PopulateIndexCallback callback(backingStore, indexKeyPath, databaseId, objectStoreId, indexId);
+ if (!backingStore.forEachObjectStoreRecord(databaseId, objectStoreId, callback))
return false;
return true;
}
@@ -360,7 +375,7 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons
return 0;
}
- RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_backingStore.get(), name, m_name, keyPath, unique);
+ RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_backingStore.get(), m_databaseId, this, name, m_name, keyPath, unique);
ASSERT(index->name() == name);
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
@@ -378,14 +393,14 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons
void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
int64_t id;
- if (!objectStore->m_backingStore->createIndex(objectStore->m_id, index->name(), index->keyPath(), index->unique(), id)) {
+ if (!objectStore->m_backingStore->createIndex(objectStore->m_databaseId, objectStore->id(), index->name(), index->keyPath(), index->unique(), id)) {
transaction->abort();
return;
}
index->setId(id);
- if (!populateIndex(*objectStore->m_backingStore, objectStore->m_id, id, index->keyPath())) {
+ if (!populateIndex(*objectStore->m_backingStore, objectStore->m_databaseId, objectStore->m_id, id, index->keyPath())) {
transaction->abort();
return;
}
@@ -428,7 +443,7 @@ void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBa
void IDBObjectStoreBackendImpl::deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- objectStore->m_backingStore->deleteIndex(index->id());
+ objectStore->m_backingStore->deleteIndex(objectStore->m_databaseId, objectStore->id(), index->id());
transaction->didCompleteTaskEvents();
}
@@ -446,7 +461,7 @@ void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, Pass
{
IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection);
- RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->id(), range.get(), direction);
+ RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->m_databaseId, objectStore->id(), range.get(), direction);
if (!backingStoreCursor) {
callbacks->onSuccess(SerializedScriptValue::nullValue());
return;
@@ -462,14 +477,14 @@ void IDBObjectStoreBackendImpl::loadIndexes()
Vector<String> names;
Vector<String> keyPaths;
Vector<bool> uniqueFlags;
- m_backingStore->getIndexes(m_id, ids, names, keyPaths, uniqueFlags);
+ m_backingStore->getIndexes(m_databaseId, m_id, ids, names, keyPaths, uniqueFlags);
ASSERT(names.size() == ids.size());
ASSERT(keyPaths.size() == ids.size());
ASSERT(uniqueFlags.size() == ids.size());
for (size_t i = 0; i < ids.size(); i++)
- m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i]));
+ m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), m_databaseId, this, ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i]));
}
void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index)
@@ -490,7 +505,7 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::genAutoIncrementKey()
if (m_autoIncrementNumber > 0)
return IDBKey::createNumber(m_autoIncrementNumber++);
- m_autoIncrementNumber = static_cast<int>(m_backingStore->nextAutoIncrementNumber(id()));
+ m_autoIncrementNumber = static_cast<int>(m_backingStore->nextAutoIncrementNumber(m_databaseId, id()));
return IDBKey::createNumber(m_autoIncrementNumber++);
}
diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h
index 4479d1e..6bf95cd 100644
--- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h
+++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -42,13 +42,13 @@ class ScriptExecutionContext;
class IDBObjectStoreBackendImpl : public IDBObjectStoreBackendInterface {
public:
- static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
+ static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
{
- return adoptRef(new IDBObjectStoreBackendImpl(backingStore, id, name, keyPath, autoIncrement));
+ return adoptRef(new IDBObjectStoreBackendImpl(backingStore, databaseId, id, name, keyPath, autoIncrement));
}
- static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement)
+ static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement)
{
- return adoptRef(new IDBObjectStoreBackendImpl(backingStore, name, keyPath, autoIncrement));
+ return adoptRef(new IDBObjectStoreBackendImpl(backingStore, databaseId, name, keyPath, autoIncrement));
}
virtual ~IDBObjectStoreBackendImpl();
@@ -59,6 +59,7 @@ public:
return m_id;
}
void setId(int64_t id) { m_id = id; }
+ int64_t databaseId() const { return m_databaseId; }
virtual String name() const { return m_name; }
virtual String keyPath() const { return m_keyPath; }
@@ -77,8 +78,8 @@ public:
virtual void openCursor(PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&);
private:
- IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& keyPath, bool autoIncrement);
- IDBObjectStoreBackendImpl(IDBBackingStore*, const String& name, const String& keyPath, bool autoIncrement);
+ IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t databaseId, int64_t id, const String& name, const String& keyPath, bool autoIncrement);
+ IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement);
void loadIndexes();
PassRefPtr<IDBKey> genAutoIncrementKey();
@@ -99,6 +100,7 @@ private:
RefPtr<IDBBackingStore> m_backingStore;
+ int64_t m_databaseId;
int64_t m_id;
String m_name;
String m_keyPath;
diff --git a/Source/WebCore/storage/IDBSQLiteBackingStore.cpp b/Source/WebCore/storage/IDBSQLiteBackingStore.cpp
index e43b7a3..f42a1e1 100644
--- a/Source/WebCore/storage/IDBSQLiteBackingStore.cpp
+++ b/Source/WebCore/storage/IDBSQLiteBackingStore.cpp
@@ -173,7 +173,7 @@ PassRefPtr<IDBBackingStore> IDBSQLiteBackingStore::open(SecurityOrigin* security
if (!pathBase.isEmpty()) {
if (!makeAllDirectories(pathBase)) {
// FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out.
- LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data());
+ LOG_ERROR("Unable to create Indexed DB database path %s", pathBase.utf8().data());
return 0;
}
path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb");
@@ -263,7 +263,7 @@ void IDBSQLiteBackingStore::getObjectStores(int64_t databaseId, Vector<int64_t>&
}
}
-bool IDBSQLiteBackingStore::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId)
+bool IDBSQLiteBackingStore::createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId)
{
SQLiteStatement query(m_db, "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)");
if (query.prepare() != SQLResultOk)
@@ -291,7 +291,7 @@ static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id)
ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
}
-void IDBSQLiteBackingStore::deleteObjectStore(int64_t objectStoreId)
+void IDBSQLiteBackingStore::deleteObjectStore(int64_t, int64_t objectStoreId)
{
doDelete(m_db, "DELETE FROM ObjectStores WHERE id = ?", objectStoreId);
doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId);
@@ -299,6 +299,26 @@ void IDBSQLiteBackingStore::deleteObjectStore(int64_t objectStoreId)
doDelete(m_db, "DELETE FROM Indexes WHERE objectStoreId = ?", objectStoreId);
}
+namespace {
+class SQLiteRecordIdentifier : public IDBBackingStore::ObjectStoreRecordIdentifier {
+public:
+ static PassRefPtr<SQLiteRecordIdentifier> create() { return adoptRef(new SQLiteRecordIdentifier()); }
+ static PassRefPtr<SQLiteRecordIdentifier> create(int64_t id) { return adoptRef(new SQLiteRecordIdentifier(id)); }
+ virtual bool isValid() const { return m_id != -1; }
+ int64_t id() const { return m_id; }
+ void setId(int64_t id) { m_id = id; }
+private:
+ SQLiteRecordIdentifier() : m_id(-1) { }
+ SQLiteRecordIdentifier(int64_t id) : m_id(id) { }
+ int64_t m_id;
+};
+}
+
+PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> IDBSQLiteBackingStore::createInvalidRecordIdentifier()
+{
+ return SQLiteRecordIdentifier::create();
+}
+
static String whereSyntaxForKey(const IDBKey& key, String qualifiedTableName = "")
{
switch (key.type()) {
@@ -373,7 +393,7 @@ static String upperCursorWhereFragment(const IDBKey& key, String comparisonOpera
return "";
}
-String IDBSQLiteBackingStore::getObjectStoreRecord(int64_t objectStoreId, const IDBKey& key)
+String IDBSQLiteBackingStore::getObjectStoreRecord(int64_t, int64_t objectStoreId, const IDBKey& key)
{
SQLiteStatement query(m_db, "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE objectStoreId = ? AND " + whereSyntaxForKey(key));
bool ok = query.prepare() == SQLResultOk;
@@ -422,58 +442,54 @@ static void bindKeyToQueryWithNulls(SQLiteStatement& query, int baseColumn, cons
}
}
-bool IDBSQLiteBackingStore::putObjectStoreRecord(int64_t objectStoreId, const IDBKey& key, const String& value, int64_t& rowId, bool invalidRowId)
+bool IDBSQLiteBackingStore::putObjectStoreRecord(int64_t, int64_t objectStoreId, const IDBKey& key, const String& value, ObjectStoreRecordIdentifier* recordIdentifier)
{
- String sql = !invalidRowId ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?"
- : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)";
+ SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<SQLiteRecordIdentifier*>(recordIdentifier);
+
+ String sql = sqliteRecordIdentifier->isValid() ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?"
+ : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)";
SQLiteStatement query(m_db, sql);
if (query.prepare() != SQLResultOk)
return false;
bindKeyToQueryWithNulls(query, 1, key);
query.bindBlob(4, value);
- if (!invalidRowId)
- query.bindInt64(5, rowId);
+ if (sqliteRecordIdentifier->isValid())
+ query.bindInt64(5, sqliteRecordIdentifier->id());
else
query.bindInt64(5, objectStoreId);
if (query.step() != SQLResultDone)
return false;
- if (invalidRowId)
- rowId = m_db.lastInsertRowID();
+ if (!sqliteRecordIdentifier->isValid())
+ sqliteRecordIdentifier->setId(m_db.lastInsertRowID());
return true;
}
-void IDBSQLiteBackingStore::clearObjectStore(int64_t objectStoreId)
+void IDBSQLiteBackingStore::clearObjectStore(int64_t, int64_t objectStoreId)
{
doDelete(m_db, "DELETE FROM IndexData WHERE objectStoreDataId IN (SELECT id FROM ObjectStoreData WHERE objectStoreId = ?)", objectStoreId);
doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId);
}
-void IDBSQLiteBackingStore::deleteObjectStoreRecord(int64_t, int64_t objectStoreDataId)
+void IDBSQLiteBackingStore::deleteObjectStoreRecord(int64_t, int64_t objectStoreId, const ObjectStoreRecordIdentifier* recordIdentifier)
{
+ const SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<const SQLiteRecordIdentifier*>(recordIdentifier);
+ ASSERT(sqliteRecordIdentifier->isValid());
+
SQLiteStatement osQuery(m_db, "DELETE FROM ObjectStoreData WHERE id = ?");
bool ok = osQuery.prepare() == SQLResultOk;
ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
- osQuery.bindInt64(1, objectStoreDataId);
+ osQuery.bindInt64(1, sqliteRecordIdentifier->id());
ok = osQuery.step() == SQLResultDone;
ASSERT_UNUSED(ok, ok);
-
- SQLiteStatement indexQuery(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?");
- ok = indexQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- indexQuery.bindInt64(1, objectStoreDataId);
-
- ok = indexQuery.step() == SQLResultDone;
- ASSERT_UNUSED(ok, ok);
}
-double IDBSQLiteBackingStore::nextAutoIncrementNumber(int64_t objectStoreId)
+double IDBSQLiteBackingStore::nextAutoIncrementNumber(int64_t, int64_t objectStoreId)
{
SQLiteStatement query(m_db, "SELECT max(keyNumber) + 1 FROM ObjectStoreData WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL");
bool ok = query.prepare() == SQLResultOk;
@@ -487,8 +503,10 @@ double IDBSQLiteBackingStore::nextAutoIncrementNumber(int64_t objectStoreId)
return query.getColumnDouble(0);
}
-bool IDBSQLiteBackingStore::keyExistsInObjectStore(int64_t objectStoreId, const IDBKey& key, int64_t& foundObjectStoreDataId)
+bool IDBSQLiteBackingStore::keyExistsInObjectStore(int64_t, int64_t objectStoreId, const IDBKey& key, ObjectStoreRecordIdentifier* foundRecordIdentifier)
{
+ SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<SQLiteRecordIdentifier*>(foundRecordIdentifier);
+
String sql = String("SELECT id FROM ObjectStoreData WHERE objectStoreId = ? AND ") + whereSyntaxForKey(key);
SQLiteStatement query(m_db, sql);
bool ok = query.prepare() == SQLResultOk;
@@ -500,11 +518,11 @@ bool IDBSQLiteBackingStore::keyExistsInObjectStore(int64_t objectStoreId, const
if (query.step() != SQLResultRow)
return false;
- foundObjectStoreDataId = query.getColumnInt64(0);
+ sqliteRecordIdentifier->setId(query.getColumnInt64(0));
return true;
}
-bool IDBSQLiteBackingStore::forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback& callback)
+bool IDBSQLiteBackingStore::forEachObjectStoreRecord(int64_t, int64_t objectStoreId, ObjectStoreRecordCallback& callback)
{
SQLiteStatement query(m_db, "SELECT id, value FROM ObjectStoreData WHERE objectStoreId = ?");
if (query.prepare() != SQLResultOk)
@@ -515,14 +533,15 @@ bool IDBSQLiteBackingStore::forEachObjectStoreRecord(int64_t objectStoreId, Obje
while (query.step() == SQLResultRow) {
int64_t objectStoreDataId = query.getColumnInt64(0);
String value = query.getColumnBlobAsString(1);
- if (!callback.callback(objectStoreDataId, value))
+ RefPtr<SQLiteRecordIdentifier> recordIdentifier = SQLiteRecordIdentifier::create(objectStoreDataId);
+ if (!callback.callback(recordIdentifier.get(), value))
return false;
}
return true;
}
-void IDBSQLiteBackingStore::getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags)
+void IDBSQLiteBackingStore::getIndexes(int64_t, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags)
{
SQLiteStatement query(m_db, "SELECT id, name, keyPath, isUnique FROM Indexes WHERE objectStoreId = ?");
bool ok = query.prepare() == SQLResultOk;
@@ -543,7 +562,7 @@ void IDBSQLiteBackingStore::getIndexes(int64_t objectStoreId, Vector<int64_t>& f
}
}
-bool IDBSQLiteBackingStore::createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId)
+bool IDBSQLiteBackingStore::createIndex(int64_t, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId)
{
SQLiteStatement query(m_db, "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)");
if (query.prepare() != SQLResultOk)
@@ -561,36 +580,41 @@ bool IDBSQLiteBackingStore::createIndex(int64_t objectStoreId, const String& nam
return true;
}
-void IDBSQLiteBackingStore::deleteIndex(int64_t indexId)
+void IDBSQLiteBackingStore::deleteIndex(int64_t, int64_t, int64_t indexId)
{
doDelete(m_db, "DELETE FROM Indexes WHERE id = ?", indexId);
doDelete(m_db, "DELETE FROM IndexData WHERE indexId = ?", indexId);
}
-bool IDBSQLiteBackingStore::putIndexDataForRecord(int64_t indexId, const IDBKey& key, int64_t objectStoreDataId)
+bool IDBSQLiteBackingStore::putIndexDataForRecord(int64_t, int64_t, int64_t indexId, const IDBKey& key, const ObjectStoreRecordIdentifier* recordIdentifier)
{
+ const SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<const SQLiteRecordIdentifier*>(recordIdentifier);
+
SQLiteStatement query(m_db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)");
if (query.prepare() != SQLResultOk)
return false;
bindKeyToQueryWithNulls(query, 1, key);
query.bindInt64(4, indexId);
- query.bindInt64(5, objectStoreDataId);
+ query.bindInt64(5, sqliteRecordIdentifier->id());
return query.step() == SQLResultDone;
}
-bool IDBSQLiteBackingStore::deleteIndexDataForRecord(int64_t objectStoreDataId)
+bool IDBSQLiteBackingStore::deleteIndexDataForRecord(int64_t, int64_t, int64_t indexId, const ObjectStoreRecordIdentifier* recordIdentifier)
{
- SQLiteStatement query(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?");
+ const SQLiteRecordIdentifier* sqliteRecordIdentifier = static_cast<const SQLiteRecordIdentifier*>(recordIdentifier);
+
+ SQLiteStatement query(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ? AND indexId = ?");
if (query.prepare() != SQLResultOk)
return false;
- query.bindInt64(1, objectStoreDataId);
+ query.bindInt64(1, sqliteRecordIdentifier->id());
+ query.bindInt64(2, indexId);
return query.step() == SQLResultDone;
}
-String IDBSQLiteBackingStore::getObjectViaIndex(int64_t indexId, const IDBKey& key)
+String IDBSQLiteBackingStore::getObjectViaIndex(int64_t, int64_t, int64_t indexId, const IDBKey& key)
{
String sql = String("SELECT ")
+ "ObjectStoreData.value "
@@ -629,7 +653,7 @@ static PassRefPtr<IDBKey> keyFromQuery(SQLiteStatement& query, int baseColumn)
return IDBKey::createNull();
}
-PassRefPtr<IDBKey> IDBSQLiteBackingStore::getPrimaryKeyViaIndex(int64_t indexId, const IDBKey& key)
+PassRefPtr<IDBKey> IDBSQLiteBackingStore::getPrimaryKeyViaIndex(int64_t, int64_t, int64_t indexId, const IDBKey& key)
{
String sql = String("SELECT ")
+ "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber "
@@ -651,7 +675,7 @@ PassRefPtr<IDBKey> IDBSQLiteBackingStore::getPrimaryKeyViaIndex(int64_t indexId,
return foundKey.release();
}
-bool IDBSQLiteBackingStore::keyExistsInIndex(int64_t indexId, const IDBKey& key)
+bool IDBSQLiteBackingStore::keyExistsInIndex(int64_t, int64_t, int64_t indexId, const IDBKey& key)
{
String sql = String("SELECT id FROM IndexData WHERE indexId = ? AND ") + whereSyntaxForKey(key);
SQLiteStatement query(m_db, sql);
@@ -682,7 +706,7 @@ public:
virtual PassRefPtr<IDBKey> key() { return m_currentKey; }
virtual PassRefPtr<IDBKey> primaryKey() { return m_currentKey; }
virtual String value() = 0;
- virtual int64_t objectStoreDataId() = 0;
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() = 0;
virtual int64_t indexDataId() = 0;
virtual void loadCurrentRow() = 0;
@@ -741,7 +765,7 @@ public:
// CursorImplCommon.
virtual String value() { return m_currentValue; }
- virtual int64_t objectStoreDataId() { return m_currentId; }
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { return SQLiteRecordIdentifier::create(m_currentId); }
virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; }
virtual void loadCurrentRow();
virtual bool currentRowExists();
@@ -779,7 +803,7 @@ public:
// CursorImplCommon
virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; }
virtual String value() { ASSERT_NOT_REACHED(); return String(); }
- virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; }
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; }
virtual int64_t indexDataId() { return m_currentId; }
virtual void loadCurrentRow();
virtual bool currentRowExists();
@@ -817,7 +841,7 @@ public:
// CursorImplCommon
virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; }
virtual String value() { return m_currentValue; }
- virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; }
+ virtual PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> objectStoreRecordIdentifier() { ASSERT_NOT_REACHED(); return 0; }
virtual int64_t indexDataId() { return m_currentId; }
virtual void loadCurrentRow();
virtual bool currentRowExists();
@@ -849,7 +873,7 @@ bool IndexCursorImpl::currentRowExists()
} // namespace
-PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction)
+PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openObjectStoreCursor(int64_t, int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction)
{
bool lowerBound = range && range->lower();
bool upperBound = range && range->upper();
@@ -886,7 +910,7 @@ PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openObjectStoreCursor
return cursor.release();
}
-PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexKeyCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexKeyCursor(int64_t, int64_t, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
{
String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
+ ("ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
@@ -926,7 +950,7 @@ PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexKeyCursor(in
return cursor.release();
}
-PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+PassRefPtr<IDBBackingStore::Cursor> IDBSQLiteBackingStore::openIndexCursor(int64_t, int64_t, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
{
String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
+ ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
diff --git a/Source/WebCore/storage/IDBSQLiteBackingStore.h b/Source/WebCore/storage/IDBSQLiteBackingStore.h
index fe0c6c1..9efa060 100644
--- a/Source/WebCore/storage/IDBSQLiteBackingStore.h
+++ b/Source/WebCore/storage/IDBSQLiteBackingStore.h
@@ -41,29 +41,30 @@ public:
virtual bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId);
virtual void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags);
- virtual bool createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId);
- virtual void deleteObjectStore(int64_t objectStoreId);
- virtual String getObjectStoreRecord(int64_t objectStoreId, const IDBKey&);
- virtual bool putObjectStoreRecord(int64_t objectStoreId, const IDBKey&, const String& value, int64_t& rowId, bool invalidRowId);
- virtual void clearObjectStore(int64_t objectStoreId);
- virtual void deleteObjectStoreRecord(int64_t objectStoreId, int64_t objectStoreDataId);
- virtual double nextAutoIncrementNumber(int64_t objectStoreId);
- virtual bool keyExistsInObjectStore(int64_t objectStoreId, const IDBKey&, int64_t& foundObjectStoreDataId);
-
- virtual bool forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback&);
-
- virtual void getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags);
- virtual bool createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId);
- virtual void deleteIndex(int64_t indexId);
- virtual bool putIndexDataForRecord(int64_t indexId, const IDBKey&, int64_t objectStoreDataId);
- virtual bool deleteIndexDataForRecord(int64_t objectStoreDataId);
- virtual String getObjectViaIndex(int64_t indexId, const IDBKey&);
- virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t indexId, const IDBKey&);
- virtual bool keyExistsInIndex(int64_t indexId, const IDBKey&);
-
- virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction);
- virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
- virtual PassRefPtr<Cursor> openIndexCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
+ virtual bool createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId);
+ virtual void deleteObjectStore(int64_t databaseId, int64_t objectStoreId);
+ virtual PassRefPtr<ObjectStoreRecordIdentifier> createInvalidRecordIdentifier();
+ virtual String getObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&);
+ virtual bool putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey&, const String& value, ObjectStoreRecordIdentifier*);
+ virtual void clearObjectStore(int64_t databaseId, int64_t objectStoreId);
+ virtual void deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier*);
+ virtual double nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId);
+ virtual bool keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey&, ObjectStoreRecordIdentifier* foundRecordIdentifier);
+
+ virtual bool forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback&);
+
+ virtual void getIndexes(int64_t databaseId, int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags);
+ virtual bool createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId);
+ virtual void deleteIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
+ virtual bool putIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const ObjectStoreRecordIdentifier*);
+ virtual bool deleteIndexDataForRecord(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const ObjectStoreRecordIdentifier*);
+ virtual String getObjectViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&);
+ virtual PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&);
+ virtual bool keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&);
+
+ virtual PassRefPtr<Cursor> openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction);
+ virtual PassRefPtr<Cursor> openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
+ virtual PassRefPtr<Cursor> openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
virtual PassRefPtr<Transaction> createTransaction();
diff --git a/Source/WebCore/storage/StorageInfo.cpp b/Source/WebCore/storage/StorageInfo.cpp
new file mode 100644
index 0000000..f3b3d08
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfo.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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"
+
+#if ENABLE(QUOTA)
+
+#include "StorageInfo.h"
+
+#include "NotImplemented.h"
+#include "StorageInfoErrorCallback.h"
+#include "StorageInfoQuotaCallback.h"
+#include "StorageInfoUsageCallback.h"
+
+namespace WebCore {
+
+class ScriptExecutionContext;
+
+StorageInfo::StorageInfo()
+{
+}
+
+StorageInfo::~StorageInfo()
+{
+}
+
+#if !PLATFORM(CHROMIUM)
+void StorageInfo::queryUsageAndQuota(ScriptExecutionContext*, int, PassRefPtr<StorageInfoUsageCallback>, PassRefPtr<StorageInfoErrorCallback>)
+{
+ notImplemented();
+}
+
+void StorageInfo::requestQuota(ScriptExecutionContext*, int, unsigned long long, PassRefPtr<StorageInfoQuotaCallback>, PassRefPtr<StorageInfoErrorCallback>)
+{
+ notImplemented();
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(QUOTA)
diff --git a/Source/WebCore/storage/StorageInfo.h b/Source/WebCore/storage/StorageInfo.h
new file mode 100644
index 0000000..b908bd8
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfo.h
@@ -0,0 +1,72 @@
+/*
+ * 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef StorageInfo_h
+#define StorageInfo_h
+
+#if ENABLE(QUOTA)
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class ScriptExecutionContext;
+class StorageInfoErrorCallback;
+class StorageInfoQuotaCallback;
+class StorageInfoUsageCallback;
+
+class StorageInfo : public RefCounted<StorageInfo> {
+public:
+ enum {
+ TEMPORARY,
+ PERSISTENT,
+ };
+
+ static PassRefPtr<StorageInfo> create()
+ {
+ return adoptRef(new StorageInfo());
+ }
+
+ void queryUsageAndQuota(ScriptExecutionContext*, int storageType, PassRefPtr<StorageInfoUsageCallback>, PassRefPtr<StorageInfoErrorCallback>);
+
+ void requestQuota(ScriptExecutionContext*, int storageType, unsigned long long newQuotaInBytes, PassRefPtr<StorageInfoQuotaCallback>, PassRefPtr<StorageInfoErrorCallback>);
+
+ ~StorageInfo();
+
+private:
+ StorageInfo();
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(QUOTA)
+
+#endif // StorageInfo_h
diff --git a/Source/WebCore/storage/StorageInfo.idl b/Source/WebCore/storage/StorageInfo.idl
new file mode 100644
index 0000000..c75b72b
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfo.idl
@@ -0,0 +1,37 @@
+/*
+ * 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 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.
+ */
+
+module storage {
+ interface [
+ Conditional=QUOTA,
+ OmitConstructor,
+ GenerateNativeConverter
+ ] StorageInfo {
+ const unsigned short TEMPORARY = 0;
+ const unsigned short PERSISTENT = 1;
+
+ [CallWith=ScriptExecutionContext] void queryUsageAndQuota(in unsigned short storageType, in [Callback, Optional] StorageInfoUsageCallback usageCallback, in [Callback, Optional] StorageInfoErrorCallback errorCallback);
+ };
+}
diff --git a/Source/WebCore/storage/StorageInfoErrorCallback.h b/Source/WebCore/storage/StorageInfoErrorCallback.h
new file mode 100644
index 0000000..6e3d75e
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfoErrorCallback.h
@@ -0,0 +1,52 @@
+/*
+ * 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef StorageInfoErrorCallback_h
+#define StorageInfoErrorCallback_h
+
+#if ENABLE(QUOTA)
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class DOMCoreException;
+
+class StorageInfoErrorCallback : public RefCounted<StorageInfoErrorCallback> {
+public:
+ virtual ~StorageInfoErrorCallback() { }
+ virtual bool handleEvent(DOMCoreException*) = 0;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(QUOTA)
+
+#endif // StorageInfoErrorCallback_h
diff --git a/Source/WebCore/storage/StorageInfoErrorCallback.idl b/Source/WebCore/storage/StorageInfoErrorCallback.idl
new file mode 100644
index 0000000..66c3316
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfoErrorCallback.idl
@@ -0,0 +1,38 @@
+/*
+ * 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+module storage {
+ interface [
+ Conditional=QUOTA,
+ Callback
+ ] StorageInfoErrorCallback {
+ boolean handleEvent(in DOMCoreException error);
+ };
+}
diff --git a/Source/WebCore/storage/StorageInfoQuotaCallback.h b/Source/WebCore/storage/StorageInfoQuotaCallback.h
new file mode 100644
index 0000000..f6fde99
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfoQuotaCallback.h
@@ -0,0 +1,50 @@
+/*
+ * 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef StorageInfoQuotaCallback_h
+#define StorageInfoQuotaCallback_h
+
+#if ENABLE(QUOTA)
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class StorageInfoQuotaCallback : public RefCounted<StorageInfoQuotaCallback> {
+public:
+ virtual ~StorageInfoQuotaCallback() { }
+ virtual bool handleEvent(unsigned long long grantedQuotaInBytes) = 0;
+};
+
+} // namespace
+
+#endif // ENABLE(QUOTA)
+
+#endif // StorageInfoQuotaCallback_h
diff --git a/Source/WebCore/storage/StorageInfoUsageCallback.h b/Source/WebCore/storage/StorageInfoUsageCallback.h
new file mode 100644
index 0000000..8382611
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfoUsageCallback.h
@@ -0,0 +1,50 @@
+/*
+ * 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#ifndef StorageInfoUsageCallback_h
+#define StorageInfoUsageCallback_h
+
+#if ENABLE(QUOTA)
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class StorageInfoUsageCallback : public RefCounted<StorageInfoUsageCallback> {
+public:
+ virtual ~StorageInfoUsageCallback() { }
+ virtual bool handleEvent(unsigned long long currentUsageInBytes, unsigned long long currentQuotaInBytes) = 0;
+};
+
+} // namespace
+
+#endif // ENABLE(QUOTA)
+
+#endif // StorageInfoUsageCallback_h
diff --git a/Source/WebCore/storage/StorageInfoUsageCallback.idl b/Source/WebCore/storage/StorageInfoUsageCallback.idl
new file mode 100644
index 0000000..dfa58e6
--- /dev/null
+++ b/Source/WebCore/storage/StorageInfoUsageCallback.idl
@@ -0,0 +1,39 @@
+/*
+ * 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+module storage {
+ interface [
+ Conditional=QUOTA,
+ Callback
+ ] StorageInfoUsageCallback {
+ boolean handleEvent(in unsigned long long currentUsageInBytes, in unsigned long long currentQuotaInBytes);
+ };
+}
+
diff --git a/Source/WebCore/storage/StorageTracker.cpp b/Source/WebCore/storage/StorageTracker.cpp
index e18b4b8..b9ce65f 100644
--- a/Source/WebCore/storage/StorageTracker.cpp
+++ b/Source/WebCore/storage/StorageTracker.cpp
@@ -232,8 +232,10 @@ void StorageTracker::syncFileSystemAndTrackerDatabase()
// Delete stale StorageTracker records.
OriginSet::const_iterator setEnd = originSetCopy.end();
for (OriginSet::const_iterator it = originSetCopy.begin(); it != setEnd; ++it) {
- if (!foundOrigins.contains(*it))
- syncDeleteOrigin(*it);
+ if (!foundOrigins.contains(*it)) {
+ RefPtr<StringImpl> originIdentifier = (*it).threadsafeCopy().impl();
+ callOnMainThread(deleteOriginOnMainThread, originIdentifier.release().leakRef());
+ }
}
}
@@ -376,10 +378,33 @@ void StorageTracker::syncDeleteAllOrigins()
if (m_database.isOpen())
m_database.close();
- SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath());
+ if (!SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath())) {
+ // In the case where it is not possible to delete the database file (e.g some other program
+ // like a virus scanner is accessing it), make sure to remove all entries.
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return;
+ SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins");
+ if (deleteStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare deletion of all origins");
+ return;
+ }
+ if (!deleteStatement.executeCommand()) {
+ LOG_ERROR("Unable to execute deletion of all origins");
+ return;
+ }
+ }
SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_storageDirectoryPath);
}
+void StorageTracker::deleteOriginOnMainThread(void* originIdentifier)
+{
+ ASSERT(isMainThread());
+
+ String identifier = adoptRef(reinterpret_cast<StringImpl*>(originIdentifier));
+ tracker().deleteOrigin(identifier);
+}
+
void StorageTracker::deleteOrigin(const String& originIdentifier)
{
deleteOrigin(SecurityOrigin::createFromDatabaseIdentifier(originIdentifier).get());
diff --git a/Source/WebCore/storage/StorageTracker.h b/Source/WebCore/storage/StorageTracker.h
index 04a821b..714576d 100644
--- a/Source/WebCore/storage/StorageTracker.h
+++ b/Source/WebCore/storage/StorageTracker.h
@@ -86,6 +86,7 @@ private:
bool canDeleteOrigin(const String& originIdentifier);
void willDeleteOrigin(const String& originIdentifier);
void willDeleteAllOrigins();
+ static void deleteOriginOnMainThread(void* originIdentifier);
void originFilePaths(Vector<String>& paths);