summaryrefslogtreecommitdiffstats
path: root/content/browser
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser')
-rw-r--r--content/browser/in_process_webkit/indexed_db_database_callbacks.cc44
-rw-r--r--content/browser/in_process_webkit/indexed_db_dispatcher_host.cc461
-rw-r--r--content/browser/in_process_webkit/indexed_db_dispatcher_host.h28
-rw-r--r--content/browser/indexed_db/DEPS3
-rw-r--r--content/browser/indexed_db/indexed_db.h34
-rw-r--r--content/browser/indexed_db/indexed_db_backing_store.cc2543
-rw-r--r--content/browser/indexed_db/indexed_db_backing_store.h319
-rw-r--r--content/browser/indexed_db/indexed_db_backing_store_unittest.cc403
-rw-r--r--content/browser/indexed_db/indexed_db_callbacks_wrapper.cc216
-rw-r--r--content/browser/indexed_db/indexed_db_callbacks_wrapper.h95
-rw-r--r--content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc74
-rw-r--r--content/browser/indexed_db/indexed_db_context_impl.cc84
-rw-r--r--content/browser/indexed_db/indexed_db_context_impl.h4
-rw-r--r--content/browser/indexed_db/indexed_db_cursor.h37
-rw-r--r--content/browser/indexed_db/indexed_db_cursor_impl.cc223
-rw-r--r--content/browser/indexed_db/indexed_db_cursor_impl.h98
-rw-r--r--content/browser/indexed_db/indexed_db_database.h131
-rw-r--r--content/browser/indexed_db/indexed_db_database_callbacks_wrapper.cc42
-rw-r--r--content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h43
-rw-r--r--content/browser/indexed_db/indexed_db_database_error.h43
-rw-r--r--content/browser/indexed_db/indexed_db_database_impl.cc1823
-rw-r--r--content/browser/indexed_db/indexed_db_database_impl.h204
-rw-r--r--content/browser/indexed_db/indexed_db_factory.h47
-rw-r--r--content/browser/indexed_db/indexed_db_factory_impl.cc197
-rw-r--r--content/browser/indexed_db/indexed_db_factory_impl.h77
-rw-r--r--content/browser/indexed_db/indexed_db_index_writer.cc169
-rw-r--r--content/browser/indexed_db/indexed_db_index_writer.h82
-rw-r--r--content/browser/indexed_db/indexed_db_internals_ui.cc4
-rw-r--r--content/browser/indexed_db/indexed_db_internals_ui.h4
-rw-r--r--content/browser/indexed_db/indexed_db_leveldb_coding.cc1884
-rw-r--r--content/browser/indexed_db/indexed_db_leveldb_coding.h467
-rw-r--r--content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc755
-rw-r--r--content/browser/indexed_db/indexed_db_metadata.cc39
-rw-r--r--content/browser/indexed_db/indexed_db_metadata.h84
-rw-r--r--content/browser/indexed_db/indexed_db_quota_client.cc70
-rw-r--r--content/browser/indexed_db/indexed_db_quota_client.h6
-rw-r--r--content/browser/indexed_db/indexed_db_quota_client_unittest.cc56
-rw-r--r--content/browser/indexed_db/indexed_db_tracing.h11
-rw-r--r--content/browser/indexed_db/indexed_db_transaction.cc308
-rw-r--r--content/browser/indexed_db/indexed_db_transaction.h150
-rw-r--r--content/browser/indexed_db/indexed_db_transaction_coordinator.cc135
-rw-r--r--content/browser/indexed_db/indexed_db_transaction_coordinator.h50
-rw-r--r--content/browser/indexed_db/indexed_db_unittest.cc34
-rw-r--r--content/browser/indexed_db/leveldb/avltree.h979
-rw-r--r--content/browser/indexed_db/leveldb/fixed_array.h62
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_comparator.h24
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_database.cc362
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_database.h75
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_iterator.h26
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_slice.h38
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_transaction.cc490
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_transaction.h179
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_unittest.cc193
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_write_batch.cc37
-rw-r--r--content/browser/indexed_db/leveldb/leveldb_write_batch.h38
-rw-r--r--content/browser/indexed_db/list_set.h158
-rw-r--r--content/browser/indexed_db/list_set_unittest.cc239
-rw-r--r--content/browser/indexed_db/webidbcursor_impl.cc51
-rw-r--r--content/browser/indexed_db/webidbcursor_impl.h33
-rw-r--r--content/browser/indexed_db/webidbdatabase_impl.cc278
-rw-r--r--content/browser/indexed_db/webidbdatabase_impl.h110
-rw-r--r--content/browser/indexed_db/webidbfactory_impl.cc68
-rw-r--r--content/browser/indexed_db/webidbfactory_impl.h41
63 files changed, 14652 insertions, 410 deletions
diff --git a/content/browser/in_process_webkit/indexed_db_database_callbacks.cc b/content/browser/in_process_webkit/indexed_db_database_callbacks.cc
index 9a7cc10..a57193d 100644
--- a/content/browser/in_process_webkit/indexed_db_database_callbacks.cc
+++ b/content/browser/in_process_webkit/indexed_db_database_callbacks.cc
@@ -4,6 +4,7 @@
#include "content/browser/in_process_webkit/indexed_db_database_callbacks.h"
+#include "base/memory/scoped_vector.h"
#include "content/browser/in_process_webkit/indexed_db_dispatcher_host.h"
#include "content/common/indexed_db/indexed_db_messages.h"
@@ -15,46 +16,39 @@ IndexedDBDatabaseCallbacks::IndexedDBDatabaseCallbacks(
int ipc_database_callbacks_id)
: dispatcher_host_(dispatcher_host),
ipc_thread_id_(ipc_thread_id),
- ipc_database_callbacks_id_(ipc_database_callbacks_id) {
-}
+ ipc_database_callbacks_id_(ipc_database_callbacks_id) {}
-IndexedDBDatabaseCallbacks::~IndexedDBDatabaseCallbacks() {
-}
+IndexedDBDatabaseCallbacks::~IndexedDBDatabaseCallbacks() {}
void IndexedDBDatabaseCallbacks::onForcedClose() {
- dispatcher_host_->Send(
- new IndexedDBMsg_DatabaseCallbacksForcedClose(
- ipc_thread_id_,
- ipc_database_callbacks_id_));
+ dispatcher_host_->Send(new IndexedDBMsg_DatabaseCallbacksForcedClose(
+ ipc_thread_id_, ipc_database_callbacks_id_));
}
void IndexedDBDatabaseCallbacks::onVersionChange(long long old_version,
long long new_version) {
- dispatcher_host_->Send(
- new IndexedDBMsg_DatabaseCallbacksIntVersionChange(
- ipc_thread_id_,
- ipc_database_callbacks_id_,
- old_version,
- new_version));
+ dispatcher_host_->Send(new IndexedDBMsg_DatabaseCallbacksIntVersionChange(
+ ipc_thread_id_, ipc_database_callbacks_id_, old_version, new_version));
}
void IndexedDBDatabaseCallbacks::onAbort(
long long host_transaction_id,
const WebKit::WebIDBDatabaseError& error) {
- dispatcher_host_->FinishTransaction(host_transaction_id, false);
- dispatcher_host_->Send(
- new IndexedDBMsg_DatabaseCallbacksAbort(
- ipc_thread_id_, ipc_database_callbacks_id_,
- dispatcher_host_->RendererTransactionId(host_transaction_id),
- error.code(), error.message()));
+ dispatcher_host_->FinishTransaction(host_transaction_id, false);
+ dispatcher_host_->Send(new IndexedDBMsg_DatabaseCallbacksAbort(
+ ipc_thread_id_,
+ ipc_database_callbacks_id_,
+ dispatcher_host_->RendererTransactionId(host_transaction_id),
+ error.code(),
+ error.message()));
}
void IndexedDBDatabaseCallbacks::onComplete(long long host_transaction_id) {
- dispatcher_host_->FinishTransaction(host_transaction_id, true);
- dispatcher_host_->Send(
- new IndexedDBMsg_DatabaseCallbacksComplete(
- ipc_thread_id_, ipc_database_callbacks_id_,
- dispatcher_host_->RendererTransactionId(host_transaction_id)));
+ dispatcher_host_->FinishTransaction(host_transaction_id, true);
+ dispatcher_host_->Send(new IndexedDBMsg_DatabaseCallbacksComplete(
+ ipc_thread_id_,
+ ipc_database_callbacks_id_,
+ dispatcher_host_->RendererTransactionId(host_transaction_id)));
}
} // namespace content
diff --git a/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc b/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc
index 8532d21..6d640c5 100644
--- a/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc
+++ b/content/browser/in_process_webkit/indexed_db_dispatcher_host.cc
@@ -41,26 +41,24 @@ using WebKit::WebIDBCallbacks;
using WebKit::WebIDBCursor;
using WebKit::WebIDBDatabase;
using WebKit::WebIDBDatabaseError;
-using WebKit::WebIDBIndex;
using WebKit::WebIDBKey;
using WebKit::WebIDBMetadata;
-using WebKit::WebIDBObjectStore;
using WebKit::WebString;
using WebKit::WebVector;
namespace content {
namespace {
-template <class T>
-void DeleteOnWebKitThread(T* obj) {
- if (!BrowserThread::DeleteSoon(BrowserThread::WEBKIT_DEPRECATED,
- FROM_HERE, obj))
+template <class T> void DeleteOnWebKitThread(T* obj) {
+ if (!BrowserThread::DeleteSoon(
+ BrowserThread::WEBKIT_DEPRECATED, FROM_HERE, obj))
delete obj;
}
}
IndexedDBDispatcherHost::IndexedDBDispatcherHost(
- int ipc_process_id, IndexedDBContextImpl* indexed_db_context)
+ int ipc_process_id,
+ IndexedDBContextImpl* indexed_db_context)
: indexed_db_context_(indexed_db_context),
database_dispatcher_host_(new DatabaseDispatcherHost(this)),
cursor_dispatcher_host_(new CursorDispatcherHost(this)),
@@ -68,14 +66,14 @@ IndexedDBDispatcherHost::IndexedDBDispatcherHost(
DCHECK(indexed_db_context_.get());
}
-IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {
-}
+IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {}
void IndexedDBDispatcherHost::OnChannelClosing() {
BrowserMessageFilter::OnChannelClosing();
bool success = BrowserThread::PostTask(
- BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
+ BrowserThread::WEBKIT_DEPRECATED,
+ FROM_HERE,
base::Bind(&IndexedDBDispatcherHost::ResetDispatcherHosts, this));
if (!success)
@@ -119,12 +117,12 @@ bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message& message,
if (!handled) {
handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(IndexedDBDispatcherHost, message, *message_was_ok)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryGetDatabaseNames,
- OnIDBFactoryGetDatabaseNames)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryOpen, OnIDBFactoryOpen)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryDeleteDatabase,
- OnIDBFactoryDeleteDatabase)
- IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryGetDatabaseNames,
+ OnIDBFactoryGetDatabaseNames)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryOpen, OnIDBFactoryOpen)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryDeleteDatabase,
+ OnIDBFactoryDeleteDatabase)
+ IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
}
return handled;
@@ -151,10 +149,8 @@ int32 IndexedDBDispatcherHost::Add(WebIDBDatabase* idb_database,
return ipc_database_id;
}
-void IndexedDBDispatcherHost::RegisterTransactionId(
- int64 host_transaction_id,
- const GURL& url)
-{
+void IndexedDBDispatcherHost::RegisterTransactionId(int64 host_transaction_id,
+ const GURL& url) {
if (!database_dispatcher_host_)
return;
database_dispatcher_host_->transaction_url_map_[host_transaction_id] = url;
@@ -175,8 +171,8 @@ int64 IndexedDBDispatcherHost::HostTransactionId(int64 transaction_id) {
int64 IndexedDBDispatcherHost::RendererTransactionId(
int64 host_transaction_id) {
- DCHECK(host_transaction_id >> 32 == base::GetProcId(peer_handle())) <<
- "Invalid renderer target for transaction id";
+ DCHECK(host_transaction_id >> 32 == base::GetProcId(peer_handle()))
+ << "Invalid renderer target for transaction id";
return host_transaction_id & 0xffffffff;
}
@@ -186,8 +182,7 @@ WebIDBCursor* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id) {
}
IndexedDBDatabaseMetadata IndexedDBDispatcherHost::ConvertMetadata(
- const WebIDBMetadata& web_metadata)
-{
+ const WebIDBMetadata& web_metadata) {
IndexedDBDatabaseMetadata metadata;
metadata.id = web_metadata.id;
metadata.name = web_metadata.name;
@@ -227,8 +222,9 @@ void IndexedDBDispatcherHost::OnIDBFactoryGetDatabaseNames(
base::FilePath indexed_db_path = indexed_db_context_->data_path();
Context()->GetIDBFactory()->getDatabaseNames(
- new IndexedDBCallbacks<WebVector<WebString> >(this, params.ipc_thread_id,
- params.ipc_callbacks_id), params.database_identifier,
+ new IndexedDBCallbacks<WebVector<WebString> >(
+ this, params.ipc_thread_id, params.ipc_callbacks_id),
+ params.database_identifier,
webkit_base::FilePathToWebString(indexed_db_path));
}
@@ -246,19 +242,20 @@ void IndexedDBDispatcherHost::OnIDBFactoryOpen(
// TODO(dgrogan): Don't let a non-existing database be opened (and therefore
// created) if this origin is already over quota.
- Context()->GetIDBFactory()->open(
- params.name,
- params.version,
- host_transaction_id,
- new IndexedDBCallbacksDatabase(this, params.ipc_thread_id,
- params.ipc_callbacks_id,
- params.ipc_database_callbacks_id,
- host_transaction_id,
- origin_url),
- new IndexedDBDatabaseCallbacks(this, params.ipc_thread_id,
- params.ipc_database_callbacks_id),
- params.database_identifier,
- webkit_base::FilePathToWebString(indexed_db_path));
+ Context()->GetIDBFactory()
+ ->open(params.name,
+ params.version,
+ host_transaction_id,
+ new IndexedDBCallbacksDatabase(this,
+ params.ipc_thread_id,
+ params.ipc_callbacks_id,
+ params.ipc_database_callbacks_id,
+ host_transaction_id,
+ origin_url),
+ new IndexedDBDatabaseCallbacks(
+ this, params.ipc_thread_id, params.ipc_database_callbacks_id),
+ params.database_identifier,
+ webkit_base::FilePathToWebString(indexed_db_path));
}
void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
@@ -266,23 +263,22 @@ void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
base::FilePath indexed_db_path = indexed_db_context_->data_path();
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- Context()->GetIDBFactory()->deleteDatabase(
- params.name,
- new IndexedDBCallbacks<WebData>(this,
- params.ipc_thread_id,
- params.ipc_callbacks_id),
- params.database_identifier,
- webkit_base::FilePathToWebString(indexed_db_path));
+ Context()->GetIDBFactory()
+ ->deleteDatabase(params.name,
+ new IndexedDBCallbacks<WebData>(
+ this, params.ipc_thread_id, params.ipc_callbacks_id),
+ params.database_identifier,
+ webkit_base::FilePathToWebString(indexed_db_path));
}
-void IndexedDBDispatcherHost::FinishTransaction(
- int64 host_transaction_id, bool committed) {
+void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id,
+ bool committed) {
TransactionIDToURLMap& transaction_url_map =
- database_dispatcher_host_->transaction_url_map_;
+ database_dispatcher_host_->transaction_url_map_;
TransactionIDToSizeMap& transaction_size_map =
- database_dispatcher_host_->transaction_size_map_;
+ database_dispatcher_host_->transaction_size_map_;
TransactionIDToDatabaseIDMap& transaction_database_map =
- database_dispatcher_host_->transaction_database_map_;
+ database_dispatcher_host_->transaction_database_map_;
if (committed)
Context()->TransactionComplete(transaction_url_map[host_transaction_id]);
// It's unclear if std::map::erase(key) has defined behavior if the
@@ -305,7 +301,8 @@ void IndexedDBDispatcherHost::FinishTransaction(
template <typename ObjectType>
ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
- IDMap<ObjectType, IDMapOwnPointer>* map, int32 ipc_return_object_id) {
+ IDMap<ObjectType, IDMapOwnPointer>* map,
+ int32 ipc_return_object_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
ObjectType* return_object = map->Lookup(ipc_return_object_id);
if (!return_object) {
@@ -319,12 +316,12 @@ ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
template <typename ObjectType>
void IndexedDBDispatcherHost::DestroyObject(
- IDMap<ObjectType, IDMapOwnPointer>* map, int32 ipc_object_id) {
+ IDMap<ObjectType, IDMapOwnPointer>* map,
+ int32 ipc_object_id) {
GetOrTerminateProcess(map, ipc_object_id);
map->Remove(ipc_object_id);
}
-
//////////////////////////////////////////////////////////////////////
// IndexedDBDispatcherHost::DatabaseDispatcherHost
//
@@ -353,14 +350,16 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
++iter;
WebIDBDatabase* database = map_.Lookup(ipc_database_id);
if (database) {
- database->abort(transaction_id, WebIDBDatabaseError(
- WebKit::WebIDBDatabaseExceptionUnknownError));
+ database->abort(
+ transaction_id,
+ WebIDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError));
}
}
DCHECK(transaction_database_map_.empty());
for (WebIDBObjectIDToURLMap::iterator iter = database_url_map_.begin();
- iter != database_url_map_.end(); iter++) {
+ iter != database_url_map_.end();
+ iter++) {
WebIDBDatabase* database = map_.Lookup(iter->first);
if (database) {
database->close();
@@ -370,37 +369,33 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
}
bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
- const IPC::Message& message, bool* msg_is_ok) {
+ const IPC::Message& message,
+ bool* msg_is_ok) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(IndexedDBDispatcherHost::DatabaseDispatcherHost,
- message, *msg_is_ok)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore,
- OnCreateObjectStore)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore,
- OnDeleteObjectStore)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction,
- OnCreateTransaction)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed, OnDestroyed)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet, OnGet)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPut)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys,
- OnSetIndexKeys)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady,
- OnSetIndexesReady)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseOpenCursor, OnOpenCursor)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCount, OnCount)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteRange, OnDeleteRange)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClear, OnClear)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateIndex,
- OnCreateIndex)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteIndex,
- OnDeleteIndex)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseAbort,
- OnAbort)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit,
- OnCommit)
- IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_BEGIN_MESSAGE_MAP_EX(
+ IndexedDBDispatcherHost::DatabaseDispatcherHost, message, *msg_is_ok)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore,
+ OnCreateObjectStore)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore,
+ OnDeleteObjectStore)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction,
+ OnCreateTransaction)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed, OnDestroyed)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet, OnGet)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPut)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys, OnSetIndexKeys)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady,
+ OnSetIndexesReady)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseOpenCursor, OnOpenCursor)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCount, OnCount)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteRange, OnDeleteRange)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClear, OnClear)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateIndex, OnCreateIndex)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteIndex, OnDeleteIndex)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseAbort, OnAbort)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit, OnCommit)
+ IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
@@ -413,20 +408,22 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::Send(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore(
const IndexedDBHostMsg_DatabaseCreateObjectStore_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
- database->createObjectStore(
- host_transaction_id,
- params.object_store_id,
- params.name, params.key_path, params.auto_increment);
+ database->createObjectStore(host_transaction_id,
+ params.object_store_id,
+ params.name,
+ params.key_path,
+ params.auto_increment);
if (parent_->Context()->IsOverQuota(
- database_url_map_[params.ipc_database_id])) {
- database->abort(host_transaction_id, WebIDBDatabaseError(
- WebKit::WebIDBDatabaseExceptionQuotaError));
+ database_url_map_[params.ipc_database_id])) {
+ database->abort(
+ host_transaction_id,
+ WebIDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError));
}
}
@@ -435,8 +432,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore(
int64 transaction_id,
int64 object_store_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!database)
return;
@@ -446,22 +443,23 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction(
const IndexedDBHostMsg_DatabaseCreateTransaction_Params& params) {
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
- return;
+ return;
WebVector<long long> object_stores(params.object_store_ids.size());
for (size_t i = 0; i < params.object_store_ids.size(); ++i)
- object_stores[i] = params.object_store_ids[i];
+ object_stores[i] = params.object_store_ids[i];
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
database->createTransaction(
host_transaction_id,
- new IndexedDBDatabaseCallbacks(parent_, params.ipc_thread_id,
- params.ipc_database_callbacks_id),
- object_stores, params.mode);
+ new IndexedDBDatabaseCallbacks(
+ parent_, params.ipc_thread_id, params.ipc_database_callbacks_id),
+ object_stores,
+ params.mode);
transaction_database_map_[host_transaction_id] = params.ipc_database_id;
parent_->RegisterTransactionId(host_transaction_id,
database_url_map_[params.ipc_database_id]);
@@ -469,8 +467,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose(
int32 ipc_database_id) {
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!database)
return;
database->close();
@@ -479,8 +477,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
int32 ipc_object_id) {
WebIDBDatabase* database = map_.Lookup(ipc_object_id);
- parent_->Context()->ConnectionClosed(database_url_map_[ipc_object_id],
- database);
+ parent_->Context()
+ ->ConnectionClosed(database_url_map_[ipc_object_id], database);
database_url_map_.erase(ipc_object_id);
parent_->DestroyObject(&map_, ipc_object_id);
}
@@ -488,41 +486,42 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet(
const IndexedDBHostMsg_DatabaseGet_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
- scoped_ptr<WebIDBCallbacks> callbacks(
- new IndexedDBCallbacks<WebData>(
- parent_, params.ipc_thread_id,
- params.ipc_callbacks_id));
+ scoped_ptr<WebIDBCallbacks> callbacks(new IndexedDBCallbacks<WebData>(
+ parent_, params.ipc_thread_id, params.ipc_callbacks_id));
database->get(parent_->HostTransactionId(params.transaction_id),
params.object_store_id,
params.index_id,
- params.key_range, params.key_only, callbacks.release());
+ params.key_range,
+ params.key_only,
+ callbacks.release());
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
const IndexedDBHostMsg_DatabasePut_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
- scoped_ptr<WebIDBCallbacks> callbacks(
- new IndexedDBCallbacks<WebIDBKey>(parent_, params.ipc_thread_id,
- params.ipc_callbacks_id));
+ scoped_ptr<WebIDBCallbacks> callbacks(new IndexedDBCallbacks<WebIDBKey>(
+ parent_, params.ipc_thread_id, params.ipc_callbacks_id));
// Be careful with empty vectors.
WebData value;
if (params.value.size())
- value.assign(&params.value.front(), params.value.size());
+ value.assign(&params.value.front(), params.value.size());
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
database->put(host_transaction_id,
params.object_store_id,
- value, params.key,
- params.put_mode, callbacks.release(),
+ value,
+ params.key,
+ params.put_mode,
+ callbacks.release(),
params.index_ids,
params.index_keys);
TransactionIDToSizeMap* map =
@@ -535,22 +534,25 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys(
const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
if (params.index_ids.size() != params.index_keys.size()) {
- database->abort(host_transaction_id, WebIDBDatabaseError(
- WebKit::WebIDBDatabaseExceptionUnknownError,
- "Malformed IPC message: index_ids.size() != index_keys.size()"));
+ database->abort(
+ host_transaction_id,
+ WebIDBDatabaseError(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ "Malformed IPC message: index_ids.size() != index_keys.size()"));
return;
}
database->setIndexKeys(host_transaction_id,
params.object_store_id,
- params.primary_key, params.index_ids,
+ params.primary_key,
+ params.index_ids,
params.index_keys);
}
@@ -560,8 +562,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady(
int64 object_store_id,
const std::vector<int64>& index_ids) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!database)
return;
@@ -573,54 +575,54 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpenCursor(
const IndexedDBHostMsg_DatabaseOpenCursor_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
- scoped_ptr<WebIDBCallbacks> callbacks(
- new IndexedDBCallbacks<WebIDBCursor>(parent_, params.ipc_thread_id,
- params.ipc_callbacks_id, -1));
- database->openCursor(
- parent_->HostTransactionId(params.transaction_id),
- params.object_store_id, params.index_id,
- params.key_range, params.direction, params.key_only, params.task_type,
- callbacks.release());
+ scoped_ptr<WebIDBCallbacks> callbacks(new IndexedDBCallbacks<WebIDBCursor>(
+ parent_, params.ipc_thread_id, params.ipc_callbacks_id, -1));
+ database->openCursor(parent_->HostTransactionId(params.transaction_id),
+ params.object_store_id,
+ params.index_id,
+ params.key_range,
+ params.direction,
+ params.key_only,
+ params.task_type,
+ callbacks.release());
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCount(
const IndexedDBHostMsg_DatabaseCount_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
- scoped_ptr<WebIDBCallbacks> callbacks(
- new IndexedDBCallbacks<WebData>(
- parent_, params.ipc_thread_id,
- params.ipc_callbacks_id));
- database->count(
- parent_->HostTransactionId(params.transaction_id),
- params.object_store_id, params.index_id,
- params.key_range, callbacks.release());
+ scoped_ptr<WebIDBCallbacks> callbacks(new IndexedDBCallbacks<WebData>(
+ parent_, params.ipc_thread_id, params.ipc_callbacks_id));
+ database->count(parent_->HostTransactionId(params.transaction_id),
+ params.object_store_id,
+ params.index_id,
+ params.key_range,
+ callbacks.release());
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteRange(
const IndexedDBHostMsg_DatabaseDeleteRange_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
- scoped_ptr<WebIDBCallbacks> callbacks(
- new IndexedDBCallbacks<WebData>(
- parent_, params.ipc_thread_id,
- params.ipc_callbacks_id));
+ scoped_ptr<WebIDBCallbacks> callbacks(new IndexedDBCallbacks<WebData>(
+ parent_, params.ipc_thread_id, params.ipc_callbacks_id));
database->deleteRange(parent_->HostTransactionId(params.transaction_id),
params.object_store_id,
- params.key_range, callbacks.release());
+ params.key_range,
+ callbacks.release());
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear(
@@ -630,26 +632,25 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear(
int64 transaction_id,
int64 object_store_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!database)
return;
- scoped_ptr<WebIDBCallbacks> callbacks(
- new IndexedDBCallbacks<WebData>(
- parent_, ipc_thread_id,
- ipc_callbacks_id));
+ scoped_ptr<WebIDBCallbacks> callbacks(new IndexedDBCallbacks<WebData>(
+ parent_, ipc_thread_id, ipc_callbacks_id));
database->clear(parent_->HostTransactionId(transaction_id),
- object_store_id, callbacks.release());
+ object_store_id,
+ callbacks.release());
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnAbort(
int32 ipc_database_id,
int64 transaction_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!database)
return;
@@ -660,17 +661,19 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit(
int32 ipc_database_id,
int64 transaction_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!database)
return;
int64 host_transaction_id = parent_->HostTransactionId(transaction_id);
int64 transaction_size = transaction_size_map_[host_transaction_id];
- if (transaction_size && parent_->Context()->WouldBeOverQuota(
- transaction_url_map_[host_transaction_id], transaction_size)) {
- database->abort(host_transaction_id, WebIDBDatabaseError(
- WebKit::WebIDBDatabaseExceptionQuotaError));
+ if (transaction_size &&
+ parent_->Context()->WouldBeOverQuota(
+ transaction_url_map_[host_transaction_id], transaction_size)) {
+ database->abort(
+ host_transaction_id,
+ WebIDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError));
return;
}
@@ -680,24 +683,24 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit(
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateIndex(
const IndexedDBHostMsg_DatabaseCreateIndex_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, params.ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!database)
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
- database->createIndex(
- host_transaction_id,
- params.object_store_id,
- params.index_id,
- params.name,
- params.key_path,
- params.unique,
- params.multi_entry);
+ database->createIndex(host_transaction_id,
+ params.object_store_id,
+ params.index_id,
+ params.name,
+ params.key_path,
+ params.unique,
+ params.multi_entry);
if (parent_->Context()->IsOverQuota(
- database_url_map_[params.ipc_database_id])) {
- database->abort(host_transaction_id, WebIDBDatabaseError(
- WebKit::WebIDBDatabaseExceptionQuotaError));
+ database_url_map_[params.ipc_database_id])) {
+ database->abort(
+ host_transaction_id,
+ WebIDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError));
}
}
@@ -707,13 +710,13 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteIndex(
int64 object_store_id,
int64 index_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBDatabase* database = parent_->GetOrTerminateProcess(
- &map_, ipc_database_id);
+ WebIDBDatabase* database =
+ parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!database)
return;
- database->deleteIndex(parent_->HostTransactionId(transaction_id),
- object_store_id, index_id);
+ database->deleteIndex(
+ parent_->HostTransactionId(transaction_id), object_store_id, index_id);
}
//////////////////////////////////////////////////////////////////////
@@ -726,48 +729,45 @@ IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost(
map_.set_check_on_null_data(true);
}
-IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {
-}
+IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {}
bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
- const IPC::Message& message, bool* msg_is_ok) {
+ const IPC::Message& message,
+ bool* msg_is_ok) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_EX(IndexedDBDispatcherHost::CursorDispatcherHost,
- message, *msg_is_ok)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDelete, OnDelete)
- IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
- IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_BEGIN_MESSAGE_MAP_EX(
+ IndexedDBDispatcherHost::CursorDispatcherHost, message, *msg_is_ok)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDelete, OnDelete)
+ IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
+ IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
-
void IndexedDBDispatcherHost::CursorDispatcherHost::Send(
IPC::Message* message) {
parent_->Send(message);
}
-
void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance(
int32 ipc_cursor_id,
int32 ipc_thread_id,
int32 ipc_callbacks_id,
unsigned long count) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(
- &map_, ipc_cursor_id);
+ WebIDBCursor* idb_cursor =
+ parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
- idb_cursor->advance(count,
- new IndexedDBCallbacks<WebIDBCursor>(parent_,
- ipc_thread_id,
- ipc_callbacks_id,
- ipc_cursor_id));
+ idb_cursor->advance(
+ count,
+ new IndexedDBCallbacks<WebIDBCursor>(
+ parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
@@ -776,15 +776,15 @@ void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
int32 ipc_callbacks_id,
const IndexedDBKey& key) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_,
- ipc_cursor_id);
+ WebIDBCursor* idb_cursor =
+ parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
idb_cursor->continueFunction(
- key, new IndexedDBCallbacks<WebIDBCursor>(parent_, ipc_thread_id,
- ipc_callbacks_id,
- ipc_cursor_id));
+ key,
+ new IndexedDBCallbacks<WebIDBCursor>(
+ parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
@@ -793,22 +793,24 @@ void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
int32 ipc_callbacks_id,
int n) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_,
- ipc_cursor_id);
+ WebIDBCursor* idb_cursor =
+ parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
idb_cursor->prefetchContinue(
- n, new IndexedDBCallbacks<WebIDBCursor>(parent_, ipc_thread_id,
- ipc_callbacks_id,
- ipc_cursor_id));
+ n,
+ new IndexedDBCallbacks<WebIDBCursor>(
+ parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
- int32 ipc_cursor_id, int used_prefetches, int unused_prefetches) {
+ int32 ipc_cursor_id,
+ int used_prefetches,
+ int unused_prefetches) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_,
- ipc_cursor_id);
+ WebIDBCursor* idb_cursor =
+ parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
@@ -820,14 +822,13 @@ void IndexedDBDispatcherHost::CursorDispatcherHost::OnDelete(
int32 ipc_thread_id,
int32 ipc_callbacks_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- WebIDBCursor* idb_cursor = parent_->GetOrTerminateProcess(&map_,
- ipc_cursor_id);
+ WebIDBCursor* idb_cursor =
+ parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
- idb_cursor->deleteFunction(
- new IndexedDBCallbacks<WebData>(parent_, ipc_thread_id,
- ipc_callbacks_id));
+ idb_cursor->deleteFunction(new IndexedDBCallbacks<WebData>(
+ parent_, ipc_thread_id, ipc_callbacks_id));
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed(
diff --git a/content/browser/in_process_webkit/indexed_db_dispatcher_host.h b/content/browser/in_process_webkit/indexed_db_dispatcher_host.h
index 48a75d7..9908e31 100644
--- a/content/browser/in_process_webkit/indexed_db_dispatcher_host.h
+++ b/content/browser/in_process_webkit/indexed_db_dispatcher_host.h
@@ -30,8 +30,6 @@ struct IndexedDBHostMsg_FactoryOpen_Params;
namespace WebKit {
class WebIDBCursor;
class WebIDBDatabase;
-class WebIDBIndex;
-class WebIDBObjectStore;
struct WebIDBMetadata;
}
@@ -93,8 +91,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
// Helper templates.
template <class ReturnType>
- ReturnType* GetOrTerminateProcess(
- IDMap<ReturnType, IDMapOwnPointer>* map, int32 ipc_return_object_id);
+ ReturnType* GetOrTerminateProcess(IDMap<ReturnType, IDMapOwnPointer>* map,
+ int32 ipc_return_object_id);
template <typename ObjectType>
void DestroyObject(IDMap<ObjectType, IDMapOwnPointer>* map,
@@ -113,7 +111,7 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
~DatabaseDispatcherHost();
void CloseAll();
- bool OnMessageReceived(const IPC::Message& message, bool *msg_is_ok);
+ bool OnMessageReceived(const IPC::Message& message, bool* msg_is_ok);
void Send(IPC::Message* message);
void OnCreateObjectStore(
@@ -123,7 +121,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
int64 object_store_id);
void OnCreateTransaction(
const IndexedDBHostMsg_DatabaseCreateTransaction_Params&);
- void OnOpen(int32 ipc_database_id, int32 ipc_thread_id,
+ void OnOpen(int32 ipc_database_id,
+ int32 ipc_thread_id,
int32 ipc_callbacks_id);
void OnClose(int32 ipc_database_id);
void OnDestroyed(int32 ipc_database_id);
@@ -132,13 +131,11 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
void OnPut(const IndexedDBHostMsg_DatabasePut_Params& params);
void OnSetIndexKeys(
const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params);
- void OnSetIndexesReady(
- int32 ipc_database_id,
- int64 transaction_id,
- int64 object_store_id,
- const std::vector<int64>& ids);
- void OnOpenCursor(
- const IndexedDBHostMsg_DatabaseOpenCursor_Params& params);
+ void OnSetIndexesReady(int32 ipc_database_id,
+ int64 transaction_id,
+ int64 object_store_id,
+ const std::vector<int64>& ids);
+ void OnOpenCursor(const IndexedDBHostMsg_DatabaseOpenCursor_Params& params);
void OnCount(const IndexedDBHostMsg_DatabaseCount_Params& params);
void OnDeleteRange(
const IndexedDBHostMsg_DatabaseDeleteRange_Params& params);
@@ -169,7 +166,7 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
explicit CursorDispatcherHost(IndexedDBDispatcherHost* parent);
~CursorDispatcherHost();
- bool OnMessageReceived(const IPC::Message& message, bool *msg_is_ok);
+ bool OnMessageReceived(const IPC::Message& message, bool* msg_is_ok);
void Send(IPC::Message* message);
void OnAdvance(int32 ipc_object_store_id,
@@ -184,7 +181,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
int32 ipc_thread_id,
int32 ipc_callbacks_id,
int n);
- void OnPrefetchReset(int32 ipc_cursor_id, int used_prefetches,
+ void OnPrefetchReset(int32 ipc_cursor_id,
+ int used_prefetches,
int unused_prefetches);
void OnDelete(int32 ipc_object_store_id,
int32 ipc_thread_id,
diff --git a/content/browser/indexed_db/DEPS b/content/browser/indexed_db/DEPS
new file mode 100644
index 0000000..743a2f3
--- /dev/null
+++ b/content/browser/indexed_db/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+third_party/leveldatabase",
+]
diff --git a/content/browser/indexed_db/indexed_db.h b/content/browser/indexed_db/indexed_db.h
new file mode 100644
index 0000000..3714d40
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_H_
+
+namespace content {
+
+namespace indexed_db {
+
+enum TransactionMode {
+ TRANSACTION_READ_ONLY = 0,
+ TRANSACTION_READ_WRITE = 1,
+ TRANSACTION_VERSION_CHANGE = 2
+};
+
+enum CursorDirection {
+ CURSOR_NEXT = 0,
+ CURSOR_NEXT_NO_DUPLICATE = 1,
+ CURSOR_PREV = 2,
+ CURSOR_PREV_NO_DUPLICATE = 3,
+};
+
+enum CursorType {
+ CURSOR_KEY_AND_VALUE = 0,
+ CURSOR_KEY_ONLY
+};
+
+} // namespace indexed_db
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_H_
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
new file mode 100644
index 0000000..1596aef
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -0,0 +1,2543 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+#include "third_party/WebKit/public/platform/WebIDBKey.h"
+#include "third_party/WebKit/public/platform/WebIDBKeyPath.h"
+
+// TODO(jsbell): Make blink push the version during the open() call.
+static const uint32 kWireVersion = 2;
+
+namespace content {
+
+static const int64 kKeyGeneratorInitialNumber =
+ 1; // From the IndexedDB specification.
+
+enum IndexedDBBackingStoreErrorSource {
+ // 0 - 2 are no longer used.
+ FIND_KEY_IN_INDEX = 3,
+ GET_IDBDATABASE_METADATA,
+ GET_INDEXES,
+ GET_KEY_GENERATOR_CURRENT_NUMBER,
+ GET_OBJECT_STORES,
+ GET_RECORD,
+ KEY_EXISTS_IN_OBJECT_STORE,
+ LOAD_CURRENT_ROW,
+ SET_UP_METADATA,
+ GET_PRIMARY_KEY_VIA_INDEX,
+ KEY_EXISTS_IN_INDEX,
+ VERSION_EXISTS,
+ DELETE_OBJECT_STORE,
+ SET_MAX_OBJECT_STORE_ID,
+ SET_MAX_INDEX_ID,
+ GET_NEW_DATABASE_ID,
+ GET_NEW_VERSION_NUMBER,
+ CREATE_IDBDATABASE_METADATA,
+ DELETE_DATABASE,
+ TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
+ INTERNAL_ERROR_MAX,
+};
+
+static void RecordInternalError(const char* type,
+ IndexedDBBackingStoreErrorSource location) {
+ string16 name = ASCIIToUTF16("WebCore.IndexedDB.BackingStore.") +
+ UTF8ToUTF16(type) + ASCIIToUTF16("Error");
+ base::Histogram::FactoryGet(UTF16ToUTF8(name),
+ 1,
+ INTERNAL_ERROR_MAX,
+ INTERNAL_ERROR_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(location);
+}
+
+// Use to signal conditions that usually indicate developer error, but
+// could be caused by data corruption. A macro is used instead of an
+// inline function so that the assert and log report the line number.
+#define REPORT_ERROR(type, location) \
+ do { \
+ LOG(ERROR) << "IndexedDB " type " Error: " #location; \
+ NOTREACHED(); \
+ RecordInternalError(type, location); \
+ } while (0)
+
+#define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
+#define INTERNAL_CONSISTENCY_ERROR(location) \
+ REPORT_ERROR("Consistency", location)
+#define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
+
+static void PutBool(LevelDBTransaction* transaction,
+ const LevelDBSlice& key,
+ bool value) {
+ transaction->Put(key, EncodeBool(value));
+}
+
+template <typename DBOrTransaction>
+static bool GetInt(DBOrTransaction* db,
+ const LevelDBSlice& key,
+ int64& found_int,
+ bool& found) {
+ std::vector<char> result;
+ bool ok = db->Get(key, result, found);
+ if (!ok)
+ return false;
+ if (!found)
+ return true;
+
+ found_int = DecodeInt(result.begin(), result.end());
+ return true;
+}
+
+static void PutInt(LevelDBTransaction* transaction,
+ const LevelDBSlice& key,
+ int64 value) {
+ DCHECK_GE(value, 0);
+ transaction->Put(key, EncodeInt(value));
+}
+
+template <typename DBOrTransaction>
+WARN_UNUSED_RESULT static bool GetVarInt(DBOrTransaction* db,
+ const LevelDBSlice& key,
+ int64& found_int,
+ bool& found) {
+ std::vector<char> result;
+ bool ok = db->Get(key, result, found);
+ if (!ok)
+ return false;
+ if (!found)
+ return true;
+ if (!result.size())
+ return false;
+
+ found = DecodeVarInt(&*result.begin(), &*result.rbegin() + 1, found_int) ==
+ &*result.rbegin() + 1;
+ return true;
+}
+
+static void PutVarInt(LevelDBTransaction* transaction,
+ const LevelDBSlice& key,
+ int64 value) {
+ transaction->Put(key, EncodeVarInt(value));
+}
+
+template <typename DBOrTransaction>
+WARN_UNUSED_RESULT static bool GetString(DBOrTransaction* db,
+ const LevelDBSlice& key,
+ string16& found_string,
+ bool& found) {
+ std::vector<char> result;
+ found = false;
+ bool ok = db->Get(key, result, found);
+ if (!ok)
+ return false;
+ if (!found)
+ return true;
+ if (!result.size()) {
+ found_string.clear();
+ return true;
+ }
+
+ found_string = DecodeString(&*result.begin(), &*result.rbegin() + 1);
+ return true;
+}
+
+static void PutString(LevelDBTransaction* transaction,
+ const LevelDBSlice& key,
+ const string16& value) {
+ transaction->Put(key, EncodeString(value));
+}
+
+static void PutIDBKeyPath(LevelDBTransaction* transaction,
+ const LevelDBSlice& key,
+ const IndexedDBKeyPath& value) {
+ transaction->Put(key, EncodeIDBKeyPath(value));
+}
+
+static int CompareKeys(const LevelDBSlice& a, const LevelDBSlice& b) {
+ return Compare(a, b);
+}
+
+static int CompareIndexKeys(const LevelDBSlice& a, const LevelDBSlice& b) {
+ return Compare(a, b, true);
+}
+
+class Comparator : public LevelDBComparator {
+ public:
+ virtual int Compare(const LevelDBSlice& a, const LevelDBSlice& b) const
+ OVERRIDE {
+ return content::Compare(a, b);
+ }
+ virtual const char* Name() const OVERRIDE { return "idb_cmp1"; }
+};
+
+// 0 - Initial version.
+// 1 - Adds UserIntVersion to DatabaseMetaData.
+// 2 - Adds DataVersion to to global metadata.
+static const int64 kLatestKnownSchemaVersion = 2;
+WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
+ int64 db_schema_version = 0;
+ bool found = false;
+ bool ok = GetInt(
+ db, LevelDBSlice(SchemaVersionKey::Encode()), db_schema_version, found);
+ if (!ok)
+ return false;
+ if (!found) {
+ *known = true;
+ return true;
+ }
+ if (db_schema_version > kLatestKnownSchemaVersion) {
+ *known = false;
+ return true;
+ }
+
+ const uint32 latest_known_data_version = kWireVersion;
+ int64 db_data_version = 0;
+ ok = GetInt(
+ db, LevelDBSlice(DataVersionKey::Encode()), db_data_version, found);
+ if (!ok)
+ return false;
+ if (!found) {
+ *known = true;
+ return true;
+ }
+
+ if (db_data_version > latest_known_data_version) {
+ *known = false;
+ return true;
+ }
+
+ *known = true;
+ return true;
+}
+
+WARN_UNUSED_RESULT static bool SetUpMetadata(LevelDBDatabase* db,
+ const string16& origin) {
+ const uint32 latest_known_data_version = kWireVersion;
+ const std::vector<char> schema_version_key = SchemaVersionKey::Encode();
+ const std::vector<char> data_version_key = DataVersionKey::Encode();
+
+ scoped_refptr<LevelDBTransaction> transaction =
+ LevelDBTransaction::Create(db);
+
+ int64 db_schema_version = 0;
+ int64 db_data_version = 0;
+ bool found = false;
+ bool ok = GetInt(transaction.get(),
+ LevelDBSlice(schema_version_key),
+ db_schema_version,
+ found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(SET_UP_METADATA);
+ return false;
+ }
+ if (!found) {
+ // Initialize new backing store.
+ db_schema_version = kLatestKnownSchemaVersion;
+ PutInt(
+ transaction.get(), LevelDBSlice(schema_version_key), db_schema_version);
+ db_data_version = latest_known_data_version;
+ PutInt(transaction.get(), LevelDBSlice(data_version_key), db_data_version);
+ } else {
+ // Upgrade old backing store.
+ DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
+ if (db_schema_version < 1) {
+ db_schema_version = 1;
+ PutInt(transaction.get(),
+ LevelDBSlice(schema_version_key),
+ db_schema_version);
+ const std::vector<char> start_key =
+ DatabaseNameKey::EncodeMinKeyForOrigin(origin);
+ const std::vector<char> stop_key =
+ DatabaseNameKey::EncodeStopKeyForOrigin(origin);
+ scoped_ptr<LevelDBIterator> it = db->CreateIterator();
+ for (it->Seek(LevelDBSlice(start_key));
+ it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0;
+ it->Next()) {
+ int64 database_id = 0;
+ found = false;
+ bool ok = GetInt(transaction.get(), it->Key(), database_id, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(SET_UP_METADATA);
+ return false;
+ }
+ if (!found) {
+ INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
+ return false;
+ }
+ std::vector<char> int_version_key = DatabaseMetaDataKey::Encode(
+ database_id, DatabaseMetaDataKey::USER_INT_VERSION);
+ PutVarInt(transaction.get(),
+ LevelDBSlice(int_version_key),
+ IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
+ }
+ }
+ if (db_schema_version < 2) {
+ db_schema_version = 2;
+ PutInt(transaction.get(),
+ LevelDBSlice(schema_version_key),
+ db_schema_version);
+ db_data_version = kWireVersion;
+ PutInt(
+ transaction.get(), LevelDBSlice(data_version_key), db_data_version);
+ }
+ }
+
+ // All new values will be written using this serialization version.
+ found = false;
+ ok = GetInt(transaction.get(),
+ LevelDBSlice(data_version_key),
+ db_data_version,
+ found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(SET_UP_METADATA);
+ return false;
+ }
+ if (!found) {
+ INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
+ return false;
+ }
+ if (db_data_version < latest_known_data_version) {
+ db_data_version = latest_known_data_version;
+ PutInt(transaction.get(), LevelDBSlice(data_version_key), db_data_version);
+ }
+
+ DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
+ DCHECK_EQ(db_data_version, latest_known_data_version);
+
+ if (!transaction->Commit()) {
+ INTERNAL_WRITE_ERROR(SET_UP_METADATA);
+ return false;
+ }
+ return true;
+}
+
+template <typename DBOrTransaction>
+WARN_UNUSED_RESULT static bool GetMaxObjectStoreId(DBOrTransaction* db,
+ int64 database_id,
+ int64& max_object_store_id) {
+ const std::vector<char> max_object_store_id_key = DatabaseMetaDataKey::Encode(
+ database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
+ bool ok =
+ GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
+ return ok;
+}
+
+template <typename DBOrTransaction>
+WARN_UNUSED_RESULT static bool GetMaxObjectStoreId(
+ DBOrTransaction* db,
+ const std::vector<char>& max_object_store_id_key,
+ int64& max_object_store_id) {
+ max_object_store_id = -1;
+ bool found = false;
+ bool ok = GetInt(
+ db, LevelDBSlice(max_object_store_id_key), max_object_store_id, found);
+ if (!ok)
+ return false;
+ if (!found)
+ max_object_store_id = 0;
+
+ DCHECK_GE(max_object_store_id, 0);
+ return true;
+}
+
+class DefaultLevelDBFactory : public LevelDBFactory {
+ public:
+ virtual scoped_ptr<LevelDBDatabase> OpenLevelDB(
+ const base::FilePath& file_name,
+ const LevelDBComparator* comparator) OVERRIDE {
+ return LevelDBDatabase::Open(file_name, comparator);
+ }
+ virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE {
+ return LevelDBDatabase::Destroy(file_name);
+ }
+};
+
+IndexedDBBackingStore::IndexedDBBackingStore(
+ const string16& identifier,
+ scoped_ptr<LevelDBDatabase> db,
+ scoped_ptr<LevelDBComparator> comparator)
+ : identifier_(identifier),
+ db_(db.Pass()),
+ comparator_(comparator.Pass()),
+ weak_factory_(this) {}
+
+IndexedDBBackingStore::~IndexedDBBackingStore() {
+ // db_'s destructor uses comparator_. The order of destruction is important.
+ db_.reset();
+ comparator_.reset();
+}
+
+IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
+ const std::vector<char>& primary_key,
+ int64 version)
+ : primary_key_(primary_key), version_(version) {
+ DCHECK(!primary_key.empty());
+}
+IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
+ : primary_key_(), version_(-1) {}
+IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
+
+IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
+IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
+
+enum IndexedDBLevelDBBackingStoreOpenResult {
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_SUCCESS,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+};
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
+ const string16& database_identifier,
+ const string16& path_base_arg,
+ const string16& file_identifier) {
+ DefaultLevelDBFactory leveldb_factory;
+ return IndexedDBBackingStore::Open(
+ database_identifier, path_base_arg, file_identifier, &leveldb_factory);
+}
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
+ const string16& database_identifier,
+ const string16& path_base_arg,
+ const string16& file_identifier,
+ LevelDBFactory* leveldb_factory) {
+ IDB_TRACE("IndexedDBBackingStore::open");
+ DCHECK(!path_base_arg.empty());
+ string16 path_base = path_base_arg;
+
+ scoped_ptr<LevelDBComparator> comparator(new Comparator());
+ scoped_ptr<LevelDBDatabase> db;
+
+ if (!IsStringASCII(path_base)) {
+ base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII);
+ }
+ base::FilePath file_path_base =
+ base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(path_base));
+ if (!file_util::CreateDirectory(file_path_base)) {
+ LOG(ERROR) << "Unable to create IndexedDB database path " << path_base;
+ base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY);
+ return scoped_refptr<IndexedDBBackingStore>();
+ }
+
+ // TODO(jsbell): Rework to use FilePath throughout.
+ base::FilePath identifier_path =
+ base::FilePath::FromUTF8Unsafe(UTF16ToUTF8(database_identifier));
+ base::FilePath file_path =
+ file_path_base.Append(identifier_path).AppendASCII(".indexeddb.leveldb");
+
+ db = leveldb_factory->OpenLevelDB(file_path, comparator.get());
+
+ if (db) {
+ bool known = false;
+ bool ok = IsSchemaKnown(db.get(), &known);
+ if (!ok) {
+ LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
+ "failure to open";
+ base::Histogram::FactoryGet(
+ "WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA);
+ db.reset();
+ } else if (!known) {
+ LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
+ "as failure to open";
+ base::Histogram::FactoryGet(
+ "WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA);
+ db.reset();
+ }
+ }
+
+ if (db) {
+ base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_SUCCESS);
+ } else {
+ LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
+ bool success = leveldb_factory->DestroyLevelDB(file_path);
+ if (!success) {
+ LOG(ERROR) << "IndexedDB backing store cleanup failed";
+ base::Histogram::FactoryGet(
+ "WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED);
+ return scoped_refptr<IndexedDBBackingStore>();
+ }
+
+ LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
+ db = leveldb_factory->OpenLevelDB(file_path, comparator.get());
+ if (!db) {
+ LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
+ base::Histogram::FactoryGet(
+ "WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED);
+ return scoped_refptr<IndexedDBBackingStore>();
+ }
+ base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS);
+ }
+
+ if (!db) {
+ NOTREACHED();
+ base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR);
+ return scoped_refptr<IndexedDBBackingStore>();
+ }
+
+ return Create(file_identifier, db.Pass(), comparator.Pass());
+}
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
+ const string16& identifier) {
+ DefaultLevelDBFactory leveldb_factory;
+ return IndexedDBBackingStore::OpenInMemory(identifier, &leveldb_factory);
+}
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
+ const string16& identifier,
+ LevelDBFactory* leveldb_factory) {
+ IDB_TRACE("IndexedDBBackingStore::open_in_memory");
+
+ scoped_ptr<LevelDBComparator> comparator(new Comparator());
+ scoped_ptr<LevelDBDatabase> db =
+ LevelDBDatabase::OpenInMemory(comparator.get());
+ if (!db) {
+ LOG(ERROR) << "LevelDBDatabase::open_in_memory failed.";
+ base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_FAILED);
+ return scoped_refptr<IndexedDBBackingStore>();
+ }
+ base::Histogram::FactoryGet("WebCore.IndexedDB.BackingStore.OpenStatus",
+ 1,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX,
+ INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MAX + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(INDEXED_DB_LEVEL_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS);
+
+ return Create(identifier, db.Pass(), comparator.Pass());
+}
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
+ const string16& identifier,
+ scoped_ptr<LevelDBDatabase> db,
+ scoped_ptr<LevelDBComparator> comparator) {
+ // TODO(jsbell): Handle comparator name changes.
+ scoped_refptr<IndexedDBBackingStore> backing_store(
+ new IndexedDBBackingStore(identifier, db.Pass(), comparator.Pass()));
+
+ if (!SetUpMetadata(backing_store->db_.get(), identifier))
+ return scoped_refptr<IndexedDBBackingStore>();
+
+ return backing_store;
+}
+
+std::vector<string16> IndexedDBBackingStore::GetDatabaseNames() {
+ std::vector<string16> found_names;
+ const std::vector<char> start_key =
+ DatabaseNameKey::EncodeMinKeyForOrigin(identifier_);
+ const std::vector<char> stop_key =
+ DatabaseNameKey::EncodeStopKeyForOrigin(identifier_);
+
+ DCHECK(found_names.empty());
+
+ scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
+ for (it->Seek(LevelDBSlice(start_key));
+ it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0;
+ it->Next()) {
+ const char* p = it->Key().begin();
+ const char* limit = it->Key().end();
+
+ DatabaseNameKey database_name_key;
+ p = DatabaseNameKey::Decode(p, limit, &database_name_key);
+ DCHECK(p);
+
+ found_names.push_back(database_name_key.database_name());
+ }
+ return found_names;
+}
+
+bool IndexedDBBackingStore::GetIDBDatabaseMetaData(
+ const string16& name,
+ IndexedDBDatabaseMetadata* metadata,
+ bool& found) {
+ const std::vector<char> key = DatabaseNameKey::Encode(identifier_, name);
+ found = false;
+
+ bool ok = GetInt(db_.get(), LevelDBSlice(key), metadata->id, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
+ return false;
+ }
+ if (!found)
+ return true;
+
+ ok = GetString(db_.get(),
+ LevelDBSlice(DatabaseMetaDataKey::Encode(
+ metadata->id, DatabaseMetaDataKey::USER_VERSION)),
+ metadata->version,
+ found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
+ return false;
+ }
+ if (!found) {
+ INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA);
+ return false;
+ }
+
+ ok = GetVarInt(db_.get(),
+ LevelDBSlice(DatabaseMetaDataKey::Encode(
+ metadata->id, DatabaseMetaDataKey::USER_INT_VERSION)),
+ metadata->int_version,
+ found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
+ return false;
+ }
+ if (!found) {
+ INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA);
+ return false;
+ }
+
+ if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
+ metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
+
+ ok = GetMaxObjectStoreId(
+ db_.get(), metadata->id, metadata->max_object_store_id);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
+ return false;
+ }
+
+ return true;
+}
+
+WARN_UNUSED_RESULT static bool GetNewDatabaseId(LevelDBDatabase* db,
+ int64& new_id) {
+ scoped_refptr<LevelDBTransaction> transaction =
+ LevelDBTransaction::Create(db);
+
+ new_id = -1;
+ int64 max_database_id = -1;
+ bool found = false;
+ bool ok = GetInt(transaction.get(),
+ LevelDBSlice(MaxDatabaseIdKey::Encode()),
+ max_database_id,
+ found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
+ return false;
+ }
+ if (!found)
+ max_database_id = 0;
+
+ DCHECK_GE(max_database_id, 0);
+
+ int64 database_id = max_database_id + 1;
+ PutInt(
+ transaction.get(), LevelDBSlice(MaxDatabaseIdKey::Encode()), database_id);
+ if (!transaction->Commit()) {
+ INTERNAL_WRITE_ERROR(GET_NEW_DATABASE_ID);
+ return false;
+ }
+ new_id = database_id;
+ return true;
+}
+
+bool IndexedDBBackingStore::CreateIDBDatabaseMetaData(const string16& name,
+ const string16& version,
+ int64 int_version,
+ int64& row_id) {
+ bool ok = GetNewDatabaseId(db_.get(), row_id);
+ if (!ok)
+ return false;
+ DCHECK_GE(row_id, 0);
+
+ if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
+ int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
+
+ scoped_refptr<LevelDBTransaction> transaction =
+ LevelDBTransaction::Create(db_.get());
+ PutInt(transaction.get(),
+ LevelDBSlice(DatabaseNameKey::Encode(identifier_, name)),
+ row_id);
+ PutString(transaction.get(),
+ LevelDBSlice(DatabaseMetaDataKey::Encode(
+ row_id, DatabaseMetaDataKey::USER_VERSION)),
+ version);
+ PutVarInt(transaction.get(),
+ LevelDBSlice(DatabaseMetaDataKey::Encode(
+ row_id, DatabaseMetaDataKey::USER_INT_VERSION)),
+ int_version);
+ if (!transaction->Commit()) {
+ INTERNAL_WRITE_ERROR(CREATE_IDBDATABASE_METADATA);
+ return false;
+ }
+ return true;
+}
+
+bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 row_id,
+ int64 int_version) {
+ if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
+ int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
+ DCHECK_GE(int_version, 0) << "int_version was " << int_version;
+ PutVarInt(Transaction::LevelDBTransactionFrom(transaction),
+ LevelDBSlice(DatabaseMetaDataKey::Encode(
+ row_id, DatabaseMetaDataKey::USER_INT_VERSION)),
+ int_version);
+ return true;
+}
+
+bool IndexedDBBackingStore::UpdateIDBDatabaseMetaData(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 row_id,
+ const string16& version) {
+ PutString(Transaction::LevelDBTransactionFrom(transaction),
+ LevelDBSlice(DatabaseMetaDataKey::Encode(
+ row_id, DatabaseMetaDataKey::USER_VERSION)),
+ version);
+ return true;
+}
+
+static void DeleteRange(LevelDBTransaction* transaction,
+ const std::vector<char>& begin,
+ const std::vector<char>& end) {
+ scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
+ for (it->Seek(LevelDBSlice(begin));
+ it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(end)) < 0;
+ it->Next())
+ transaction->Remove(it->Key());
+}
+
+bool IndexedDBBackingStore::DeleteDatabase(const string16& name) {
+ IDB_TRACE("IndexedDBBackingStore::delete_database");
+ scoped_ptr<LevelDBWriteOnlyTransaction> transaction =
+ LevelDBWriteOnlyTransaction::Create(db_.get());
+
+ IndexedDBDatabaseMetadata metadata;
+ bool success = false;
+ bool ok = GetIDBDatabaseMetaData(name, &metadata, success);
+ if (!ok)
+ return false;
+ if (!success)
+ return true;
+
+ const std::vector<char> start_key = DatabaseMetaDataKey::Encode(
+ metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
+ const std::vector<char> stop_key = DatabaseMetaDataKey::Encode(
+ metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
+ scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
+ for (it->Seek(LevelDBSlice(start_key));
+ it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0;
+ it->Next())
+ transaction->Remove(it->Key());
+
+ const std::vector<char> key = DatabaseNameKey::Encode(identifier_, name);
+ transaction->Remove(LevelDBSlice(key));
+
+ if (!transaction->Commit()) {
+ INTERNAL_WRITE_ERROR(DELETE_DATABASE);
+ return false;
+ }
+ return true;
+}
+
+static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
+ const std::vector<char>& stop_key,
+ int64 object_store_id,
+ int64 meta_data_type) {
+ if (!it->IsValid() || CompareKeys(it->Key(), LevelDBSlice(stop_key)) >= 0)
+ return false;
+
+ ObjectStoreMetaDataKey meta_data_key;
+ const char* p = ObjectStoreMetaDataKey::Decode(
+ it->Key().begin(), it->Key().end(), &meta_data_key);
+ DCHECK(p);
+ if (meta_data_key.ObjectStoreId() != object_store_id)
+ return false;
+ if (meta_data_key.MetaDataType() != meta_data_type)
+ return false;
+ return true;
+}
+
+// TODO(jsbell): This should do some error handling rather than
+// plowing ahead when bad data is encountered.
+bool IndexedDBBackingStore::GetObjectStores(
+ int64 database_id,
+ IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
+ IDB_TRACE("IndexedDBBackingStore::get_object_stores");
+ if (!KeyPrefix::IsValidDatabaseId(database_id))
+ return false;
+ const std::vector<char> start_key =
+ ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
+ const std::vector<char> stop_key =
+ ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
+
+ DCHECK(object_stores->empty());
+
+ scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
+ it->Seek(LevelDBSlice(start_key));
+ while (it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0) {
+ const char* p = it->Key().begin();
+ const char* limit = it->Key().end();
+
+ ObjectStoreMetaDataKey meta_data_key;
+ p = ObjectStoreMetaDataKey::Decode(p, limit, &meta_data_key);
+ DCHECK(p);
+ if (meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
+ INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ // Possible stale metadata, but don't fail the load.
+ it->Next();
+ continue;
+ }
+
+ int64 object_store_id = meta_data_key.ObjectStoreId();
+
+ // TODO(jsbell): Do this by direct key lookup rather than iteration, to
+ // simplify.
+ string16 object_store_name =
+ DecodeString(it->Value().begin(), it->Value().end());
+
+ it->Next();
+ if (!CheckObjectStoreAndMetaDataType(it.get(),
+ stop_key,
+ object_store_id,
+ ObjectStoreMetaDataKey::KEY_PATH)) {
+ INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ break;
+ }
+ IndexedDBKeyPath key_path =
+ DecodeIDBKeyPath(it->Value().begin(), it->Value().end());
+
+ it->Next();
+ if (!CheckObjectStoreAndMetaDataType(
+ it.get(),
+ stop_key,
+ object_store_id,
+ ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
+ INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ break;
+ }
+ bool auto_increment = DecodeBool(it->Value().begin(), it->Value().end());
+
+ it->Next(); // Is evicatble.
+ if (!CheckObjectStoreAndMetaDataType(it.get(),
+ stop_key,
+ object_store_id,
+ ObjectStoreMetaDataKey::EVICTABLE)) {
+ INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ break;
+ }
+
+ it->Next(); // Last version.
+ if (!CheckObjectStoreAndMetaDataType(
+ it.get(),
+ stop_key,
+ object_store_id,
+ ObjectStoreMetaDataKey::LAST_VERSION)) {
+ INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ break;
+ }
+
+ it->Next(); // Maximum index id allocated.
+ if (!CheckObjectStoreAndMetaDataType(
+ it.get(),
+ stop_key,
+ object_store_id,
+ ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
+ INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ break;
+ }
+ int64 max_index_id = DecodeInt(it->Value().begin(), it->Value().end());
+
+ it->Next(); // [optional] has key path (is not null)
+ if (CheckObjectStoreAndMetaDataType(it.get(),
+ stop_key,
+ object_store_id,
+ ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
+ bool has_key_path = DecodeBool(it->Value().begin(), it->Value().end());
+ // This check accounts for two layers of legacy coding:
+ // (1) Initially, has_key_path was added to distinguish null vs. string.
+ // (2) Later, null vs. string vs. array was stored in the key_path itself.
+ // So this check is only relevant for string-type key_paths.
+ if (!has_key_path &&
+ (key_path.type() == WebKit::WebIDBKeyPath::StringType &&
+ !key_path.string().empty())) {
+ INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
+ break;
+ }
+ if (!has_key_path)
+ key_path = IndexedDBKeyPath();
+ it->Next();
+ }
+
+ int64 key_generator_current_number = -1;
+ if (CheckObjectStoreAndMetaDataType(
+ it.get(),
+ stop_key,
+ object_store_id,
+ ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
+ key_generator_current_number =
+ DecodeInt(it->Value().begin(), it->Value().end());
+ // TODO(jsbell): Return key_generator_current_number, cache in
+ // object store, and write lazily to backing store. For now,
+ // just assert that if it was written it was valid.
+ DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
+ it->Next();
+ }
+
+ IndexedDBObjectStoreMetadata metadata(object_store_name,
+ object_store_id,
+ key_path,
+ auto_increment,
+ max_index_id);
+ if (!GetIndexes(database_id, object_store_id, &metadata.indexes))
+ return false;
+ (*object_stores)[object_store_id] = metadata;
+ }
+ return true;
+}
+
+WARN_UNUSED_RESULT static bool SetMaxObjectStoreId(
+ LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id) {
+ const std::vector<char> max_object_store_id_key = DatabaseMetaDataKey::Encode(
+ database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
+ int64 max_object_store_id = -1;
+ bool ok = GetMaxObjectStoreId(
+ transaction, max_object_store_id_key, max_object_store_id);
+ if (!ok) {
+ INTERNAL_READ_ERROR(SET_MAX_OBJECT_STORE_ID);
+ return false;
+ }
+
+ if (object_store_id <= max_object_store_id) {
+ INTERNAL_CONSISTENCY_ERROR(SET_MAX_OBJECT_STORE_ID);
+ return false;
+ }
+ PutInt(transaction, LevelDBSlice(max_object_store_id_key), object_store_id);
+ return true;
+}
+
+bool IndexedDBBackingStore::CreateObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment) {
+ IDB_TRACE("IndexedDBBackingStore::create_object_store");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ if (!SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id))
+ return false;
+
+ const std::vector<char> name_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
+ const std::vector<char> key_path_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
+ const std::vector<char> auto_increment_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
+ const std::vector<char> evictable_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
+ const std::vector<char> last_version_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
+ const std::vector<char> max_index_id_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
+ const std::vector<char> has_key_path_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
+ const std::vector<char> key_generator_current_number_key =
+ ObjectStoreMetaDataKey::Encode(
+ database_id,
+ object_store_id,
+ ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
+ const std::vector<char> names_key =
+ ObjectStoreNamesKey::Encode(database_id, name);
+
+ PutString(leveldb_transaction, LevelDBSlice(name_key), name);
+ PutIDBKeyPath(leveldb_transaction, LevelDBSlice(key_path_key), key_path);
+ PutInt(leveldb_transaction, LevelDBSlice(auto_increment_key), auto_increment);
+ PutInt(leveldb_transaction, LevelDBSlice(evictable_key), false);
+ PutInt(leveldb_transaction, LevelDBSlice(last_version_key), 1);
+ PutInt(leveldb_transaction, LevelDBSlice(max_index_id_key), kMinimumIndexId);
+ PutBool(
+ leveldb_transaction, LevelDBSlice(has_key_path_key), !key_path.IsNull());
+ PutInt(leveldb_transaction,
+ LevelDBSlice(key_generator_current_number_key),
+ kKeyGeneratorInitialNumber);
+ PutInt(leveldb_transaction, LevelDBSlice(names_key), object_store_id);
+ return true;
+}
+
+bool IndexedDBBackingStore::DeleteObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) {
+ IDB_TRACE("IndexedDBBackingStore::delete_object_store");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+
+ string16 object_store_name;
+ bool found = false;
+ bool ok = GetString(
+ leveldb_transaction,
+ LevelDBSlice(ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::NAME)),
+ object_store_name,
+ found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(DELETE_OBJECT_STORE);
+ return false;
+ }
+ if (!found) {
+ INTERNAL_CONSISTENCY_ERROR(DELETE_OBJECT_STORE);
+ return false;
+ }
+
+ DeleteRange(
+ leveldb_transaction,
+ ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
+ ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id));
+
+ leveldb_transaction->Remove(LevelDBSlice(
+ ObjectStoreNamesKey::Encode(database_id, object_store_name)));
+
+ DeleteRange(leveldb_transaction,
+ IndexFreeListKey::Encode(database_id, object_store_id, 0),
+ IndexFreeListKey::EncodeMaxKey(database_id, object_store_id));
+ DeleteRange(leveldb_transaction,
+ IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
+ IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id));
+
+ return ClearObjectStore(transaction, database_id, object_store_id);
+}
+
+bool IndexedDBBackingStore::GetRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ std::vector<char>& record) {
+ IDB_TRACE("IndexedDBBackingStore::get_record");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+
+ const std::vector<char> leveldb_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, key);
+ std::vector<char> data;
+
+ record.clear();
+
+ bool found = false;
+ bool ok = leveldb_transaction->Get(LevelDBSlice(leveldb_key), data, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_RECORD);
+ return false;
+ }
+ if (!found)
+ return true;
+ if (!data.size()) {
+ INTERNAL_READ_ERROR(GET_RECORD);
+ return false;
+ }
+
+ int64 version;
+ const char* p = DecodeVarInt(&*data.begin(), &*data.rbegin() + 1, version);
+ if (!p) {
+ INTERNAL_READ_ERROR(GET_RECORD);
+ return false;
+ }
+
+ record.insert(record.end(), p, static_cast<const char*>(&*data.rbegin()) + 1);
+ return true;
+}
+
+WARN_UNUSED_RESULT static bool GetNewVersionNumber(
+ LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64& new_version_number) {
+ const std::vector<char> last_version_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
+
+ new_version_number = -1;
+ int64 last_version = -1;
+ bool found = false;
+ bool ok =
+ GetInt(transaction, LevelDBSlice(last_version_key), last_version, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_NEW_VERSION_NUMBER);
+ return false;
+ }
+ if (!found)
+ last_version = 0;
+
+ DCHECK_GE(last_version, 0);
+
+ int64 version = last_version + 1;
+ PutInt(transaction, LevelDBSlice(last_version_key), version);
+
+ DCHECK(version >
+ last_version); // TODO(jsbell): Think about how we want to handle
+ // the overflow scenario.
+
+ new_version_number = version;
+ return true;
+}
+
+bool IndexedDBBackingStore::PutRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ const std::vector<char>& value,
+ RecordIdentifier* record_identifier) {
+ IDB_TRACE("IndexedDBBackingStore::put_record");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ DCHECK(key.IsValid());
+
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ int64 version = -1;
+ bool ok = GetNewVersionNumber(
+ leveldb_transaction, database_id, object_store_id, version);
+ if (!ok)
+ return false;
+ DCHECK_GE(version, 0);
+ const std::vector<char> object_storedata_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, key);
+
+ std::vector<char> v(EncodeVarInt(version));
+
+ v.insert(v.end(), value.begin(), value.end());
+
+ leveldb_transaction->Put(LevelDBSlice(object_storedata_key), v);
+
+ const std::vector<char> exists_entry_key =
+ ExistsEntryKey::Encode(database_id, object_store_id, key);
+ leveldb_transaction->Put(LevelDBSlice(exists_entry_key), EncodeInt(version));
+
+ record_identifier->Reset(EncodeIDBKey(key), version);
+ return true;
+}
+
+bool IndexedDBBackingStore::ClearObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) {
+ IDB_TRACE("IndexedDBBackingStore::clear_object_store");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ const std::vector<char> start_key =
+ KeyPrefix(database_id, object_store_id).Encode();
+ const std::vector<char> stop_key =
+ KeyPrefix(database_id, object_store_id + 1).Encode();
+
+ DeleteRange(leveldb_transaction, start_key, stop_key);
+ return true;
+}
+
+bool IndexedDBBackingStore::DeleteRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const RecordIdentifier& record_identifier) {
+ IDB_TRACE("IndexedDBBackingStore::delete_record");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+
+ const std::vector<char> object_store_data_key = ObjectStoreDataKey::Encode(
+ database_id, object_store_id, record_identifier.primary_key());
+ leveldb_transaction->Remove(LevelDBSlice(object_store_data_key));
+
+ const std::vector<char> exists_entry_key = ExistsEntryKey::Encode(
+ database_id, object_store_id, record_identifier.primary_key());
+ leveldb_transaction->Remove(LevelDBSlice(exists_entry_key));
+ return true;
+}
+
+bool IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64& key_generator_current_number) {
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+
+ const std::vector<char> key_generator_current_number_key =
+ ObjectStoreMetaDataKey::Encode(
+ database_id,
+ object_store_id,
+ ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
+
+ key_generator_current_number = -1;
+ std::vector<char> data;
+
+ bool found = false;
+ bool ok = leveldb_transaction->Get(
+ LevelDBSlice(key_generator_current_number_key), data, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
+ return false;
+ }
+ if (found) {
+ key_generator_current_number = DecodeInt(data.begin(), data.end());
+ } else {
+ // Previously, the key generator state was not stored explicitly
+ // but derived from the maximum numeric key present in existing
+ // data. This violates the spec as the data may be cleared but the
+ // key generator state must be preserved.
+ // TODO(jsbell): Fix this for all stores on database open?
+ const std::vector<char> start_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
+ const std::vector<char> stop_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
+
+ scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
+ int64 max_numeric_key = 0;
+
+ for (it->Seek(LevelDBSlice(start_key));
+ it->IsValid() && CompareKeys(it->Key(), LevelDBSlice(stop_key)) < 0;
+ it->Next()) {
+ const char* p = it->Key().begin();
+ const char* limit = it->Key().end();
+
+ ObjectStoreDataKey data_key;
+ p = ObjectStoreDataKey::Decode(p, limit, &data_key);
+ DCHECK(p);
+
+ scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
+ if (user_key->type() == WebKit::WebIDBKey::NumberType) {
+ int64 n = static_cast<int64>(user_key->number());
+ if (n > max_numeric_key)
+ max_numeric_key = n;
+ }
+ }
+
+ key_generator_current_number = max_numeric_key + 1;
+ }
+
+ return true;
+}
+
+bool IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 new_number,
+ bool check_current) {
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+
+ if (check_current) {
+ int64 current_number;
+ bool ok = GetKeyGeneratorCurrentNumber(
+ transaction, database_id, object_store_id, current_number);
+ if (!ok)
+ return false;
+ if (new_number <= current_number)
+ return true;
+ }
+
+ const std::vector<char> key_generator_current_number_key =
+ ObjectStoreMetaDataKey::Encode(
+ database_id,
+ object_store_id,
+ ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
+ PutInt(leveldb_transaction,
+ LevelDBSlice(key_generator_current_number_key),
+ new_number);
+ return true;
+}
+
+bool IndexedDBBackingStore::KeyExistsInObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ RecordIdentifier* found_record_identifier,
+ bool& found) {
+ IDB_TRACE("IndexedDBBackingStore::key_exists_in_object_store");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ found = false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ const std::vector<char> leveldb_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, key);
+ std::vector<char> data;
+
+ bool ok = leveldb_transaction->Get(LevelDBSlice(leveldb_key), data, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE);
+ return false;
+ }
+ if (!found)
+ return true;
+ if (!data.size()) {
+ INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE);
+ return false;
+ }
+
+ int64 version;
+ if (DecodeVarInt(&*data.begin(), &*data.rbegin() + 1, version) == 0)
+ return false;
+
+ found_record_identifier->Reset(EncodeIDBKey(key), version);
+ return true;
+}
+
+static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
+ const std::vector<char>& stop_key,
+ int64 index_id,
+ unsigned char meta_data_type) {
+ if (!it->IsValid() || CompareKeys(it->Key(), LevelDBSlice(stop_key)) >= 0)
+ return false;
+
+ IndexMetaDataKey meta_data_key;
+ const char* p = IndexMetaDataKey::Decode(
+ it->Key().begin(), it->Key().end(), &meta_data_key);
+ DCHECK(p);
+ if (meta_data_key.IndexId() != index_id)
+ return false;
+ if (meta_data_key.meta_data_type() != meta_data_type)
+ return false;
+ return true;
+}
+
+// TODO(jsbell): This should do some error handling rather than plowing ahead
+// when bad
+// data is encountered.
+bool IndexedDBBackingStore::GetIndexes(
+ int64 database_id,
+ int64 object_store_id,
+ IndexedDBObjectStoreMetadata::IndexMap* indexes) {
+ IDB_TRACE("IndexedDBBackingStore::get_indexes");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id))
+ return false;
+ const std::vector<char> start_key =
+ IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
+ const std::vector<char> stop_key =
+ IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
+
+ DCHECK(indexes->empty());
+
+ scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
+ it->Seek(LevelDBSlice(start_key));
+ while (it->IsValid() &&
+ CompareKeys(LevelDBSlice(it->Key()), LevelDBSlice(stop_key)) < 0) {
+ const char* p = it->Key().begin();
+ const char* limit = it->Key().end();
+
+ IndexMetaDataKey meta_data_key;
+ p = IndexMetaDataKey::Decode(p, limit, &meta_data_key);
+ DCHECK(p);
+ if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
+ INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
+ // the load.
+ it->Next();
+ continue;
+ }
+
+ // TODO(jsbell): Do this by direct key lookup rather than iteration, to
+ // simplify.
+ int64 index_id = meta_data_key.IndexId();
+ string16 index_name = DecodeString(it->Value().begin(), it->Value().end());
+
+ it->Next(); // unique flag
+ if (!CheckIndexAndMetaDataKey(
+ it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
+ INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ break;
+ }
+ bool index_unique = DecodeBool(it->Value().begin(), it->Value().end());
+
+ it->Next(); // key_path
+ if (!CheckIndexAndMetaDataKey(
+ it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
+ INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
+ break;
+ }
+ IndexedDBKeyPath key_path =
+ DecodeIDBKeyPath(it->Value().begin(), it->Value().end());
+
+ it->Next(); // [optional] multi_entry flag
+ bool index_multi_entry = false;
+ if (CheckIndexAndMetaDataKey(
+ it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
+ index_multi_entry = DecodeBool(it->Value().begin(), it->Value().end());
+ it->Next();
+ }
+
+ (*indexes)[index_id] = IndexedDBIndexMetadata(
+ index_name, index_id, key_path, index_unique, index_multi_entry);
+ }
+ return true;
+}
+
+WARN_UNUSED_RESULT static bool SetMaxIndexId(LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ int64 max_index_id = -1;
+ const std::vector<char> max_index_id_key = ObjectStoreMetaDataKey::Encode(
+ database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
+ bool found = false;
+ bool ok =
+ GetInt(transaction, LevelDBSlice(max_index_id_key), max_index_id, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(SET_MAX_INDEX_ID);
+ return false;
+ }
+ if (!found)
+ max_index_id = kMinimumIndexId;
+
+ if (index_id <= max_index_id) {
+ INTERNAL_CONSISTENCY_ERROR(SET_MAX_INDEX_ID);
+ return false;
+ }
+
+ PutInt(transaction, LevelDBSlice(max_index_id_key), index_id);
+ return true;
+}
+
+bool IndexedDBBackingStore::CreateIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool is_unique,
+ bool is_multi_entry) {
+ IDB_TRACE("IndexedDBBackingStore::create_index");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ if (!SetMaxIndexId(
+ leveldb_transaction, database_id, object_store_id, index_id))
+ return false;
+
+ const std::vector<char> name_key = IndexMetaDataKey::Encode(
+ database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
+ const std::vector<char> unique_key = IndexMetaDataKey::Encode(
+ database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
+ const std::vector<char> key_path_key = IndexMetaDataKey::Encode(
+ database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
+ const std::vector<char> multi_entry_key = IndexMetaDataKey::Encode(
+ database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
+
+ PutString(leveldb_transaction, LevelDBSlice(name_key), name);
+ PutBool(leveldb_transaction, LevelDBSlice(unique_key), is_unique);
+ PutIDBKeyPath(leveldb_transaction, LevelDBSlice(key_path_key), key_path);
+ PutBool(leveldb_transaction, LevelDBSlice(multi_entry_key), is_multi_entry);
+ return true;
+}
+
+bool IndexedDBBackingStore::DeleteIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ IDB_TRACE("IndexedDBBackingStore::delete_index");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
+ return false;
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+
+ const std::vector<char> index_meta_data_start =
+ IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
+ const std::vector<char> index_meta_data_end =
+ IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
+ DeleteRange(leveldb_transaction, index_meta_data_start, index_meta_data_end);
+
+ const std::vector<char> index_data_start =
+ IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
+ const std::vector<char> index_data_end =
+ IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
+ DeleteRange(leveldb_transaction, index_data_start, index_data_end);
+ return true;
+}
+
+bool IndexedDBBackingStore::PutIndexDataForRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ const RecordIdentifier& record_identifier) {
+ IDB_TRACE("IndexedDBBackingStore::put_index_data_for_record");
+ DCHECK(key.IsValid());
+ if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
+ return false;
+
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ const std::vector<char> index_data_key =
+ IndexDataKey::Encode(database_id,
+ object_store_id,
+ index_id,
+ EncodeIDBKey(key),
+ record_identifier.primary_key());
+
+ std::vector<char> data(EncodeVarInt(record_identifier.version()));
+ const std::vector<char>& primary_key = record_identifier.primary_key();
+ data.insert(data.end(), primary_key.begin(), primary_key.end());
+
+ leveldb_transaction->Put(LevelDBSlice(index_data_key), data);
+ return true;
+}
+
+static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
+ const std::vector<char>& target,
+ std::vector<char>& found_key) {
+ scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
+ it->Seek(LevelDBSlice(target));
+
+ if (!it->IsValid()) {
+ it->SeekToLast();
+ if (!it->IsValid())
+ return false;
+ }
+
+ while (CompareIndexKeys(LevelDBSlice(it->Key()), LevelDBSlice(target)) > 0) {
+ it->Prev();
+ if (!it->IsValid())
+ return false;
+ }
+
+ do {
+ found_key.assign(it->Key().begin(), it->Key().end());
+
+ // There can be several index keys that compare equal. We want the last one.
+ it->Next();
+ } while (it->IsValid() && !CompareIndexKeys(it->Key(), LevelDBSlice(target)));
+
+ return true;
+}
+
+static bool VersionExists(LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 version,
+ const std::vector<char>& encoded_primary_key,
+ bool& exists) {
+ const std::vector<char> key =
+ ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
+ std::vector<char> data;
+
+ bool ok = transaction->Get(LevelDBSlice(key), data, exists);
+ if (!ok) {
+ INTERNAL_READ_ERROR(VERSION_EXISTS);
+ return false;
+ }
+ if (!exists)
+ return true;
+
+ exists = (DecodeInt(data.begin(), data.end()) == version);
+ return true;
+}
+
+bool IndexedDBBackingStore::FindKeyInIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ std::vector<char>& found_encoded_primary_key,
+ bool& found) {
+ IDB_TRACE("IndexedDBBackingStore::find_key_in_index");
+ DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
+
+ DCHECK(found_encoded_primary_key.empty());
+ found = false;
+
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ const std::vector<char> leveldb_key =
+ IndexDataKey::Encode(database_id, object_store_id, index_id, key);
+ scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
+ it->Seek(LevelDBSlice(leveldb_key));
+
+ for (;;) {
+ if (!it->IsValid())
+ return true;
+ if (CompareIndexKeys(it->Key(), LevelDBSlice(leveldb_key)) > 0)
+ return true;
+
+ int64 version;
+ const char* p =
+ DecodeVarInt(it->Value().begin(), it->Value().end(), version);
+ if (!p) {
+ INTERNAL_READ_ERROR(FIND_KEY_IN_INDEX);
+ return false;
+ }
+ found_encoded_primary_key.insert(
+ found_encoded_primary_key.end(), p, it->Value().end());
+
+ bool exists = false;
+ bool ok = VersionExists(leveldb_transaction,
+ database_id,
+ object_store_id,
+ version,
+ found_encoded_primary_key,
+ exists);
+ if (!ok)
+ return false;
+ if (!exists) {
+ // Delete stale index data entry and continue.
+ leveldb_transaction->Remove(it->Key());
+ it->Next();
+ continue;
+ }
+ found = true;
+ return true;
+ }
+}
+
+bool IndexedDBBackingStore::GetPrimaryKeyViaIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ scoped_ptr<IndexedDBKey>* primary_key) {
+ IDB_TRACE("IndexedDBBackingStore::get_primary_key_via_index");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
+ return false;
+
+ bool found = false;
+ std::vector<char> found_encoded_primary_key;
+ bool ok = FindKeyInIndex(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ key,
+ found_encoded_primary_key,
+ found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX);
+ return false;
+ }
+ if (!found)
+ return true;
+ if (!found_encoded_primary_key.size()) {
+ INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX);
+ return false;
+ }
+
+ DecodeIDBKey(&*found_encoded_primary_key.begin(),
+ &*found_encoded_primary_key.rbegin() + 1,
+ primary_key);
+ return true;
+}
+
+bool IndexedDBBackingStore::KeyExistsInIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& index_key,
+ scoped_ptr<IndexedDBKey>* found_primary_key,
+ bool& exists) {
+ IDB_TRACE("IndexedDBBackingStore::key_exists_in_index");
+ if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
+ return false;
+
+ exists = false;
+ std::vector<char> found_encoded_primary_key;
+ bool ok = FindKeyInIndex(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_key,
+ found_encoded_primary_key,
+ exists);
+ if (!ok) {
+ INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX);
+ return false;
+ }
+ if (!exists)
+ return true;
+ if (!found_encoded_primary_key.size()) {
+ INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX);
+ return false;
+ }
+
+ DecodeIDBKey(&*found_encoded_primary_key.begin(),
+ &*found_encoded_primary_key.rbegin() + 1,
+ found_primary_key);
+ return true;
+}
+
+IndexedDBBackingStore::Cursor::Cursor(
+ const IndexedDBBackingStore::Cursor* other)
+ : transaction_(other->transaction_),
+ cursor_options_(other->cursor_options_),
+ current_key_(new IndexedDBKey(*other->current_key_)) {
+ if (other->iterator_) {
+ iterator_ = transaction_->CreateIterator();
+
+ if (other->iterator_->IsValid()) {
+ iterator_->Seek(other->iterator_->Key());
+ DCHECK(iterator_->IsValid());
+ }
+ }
+}
+
+IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction* transaction,
+ const CursorOptions& cursor_options)
+ : transaction_(transaction), cursor_options_(cursor_options) {}
+IndexedDBBackingStore::Cursor::~Cursor() {}
+
+bool IndexedDBBackingStore::Cursor::FirstSeek() {
+ iterator_ = transaction_->CreateIterator();
+ if (cursor_options_.forward)
+ iterator_->Seek(LevelDBSlice(cursor_options_.low_key));
+ else
+ iterator_->Seek(LevelDBSlice(cursor_options_.high_key));
+
+ return ContinueFunction(0, READY);
+}
+
+bool IndexedDBBackingStore::Cursor::Advance(unsigned long count) {
+ while (count--) {
+ if (!ContinueFunction())
+ return false;
+ }
+ return true;
+}
+
+bool IndexedDBBackingStore::Cursor::ContinueFunction(const IndexedDBKey* key,
+ IteratorState next_state) {
+ // TODO(alecflett): avoid a copy here?
+ IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
+
+ bool first_iteration = true;
+
+ // When iterating with PrevNoDuplicate, spec requires that the
+ // value we yield for each key is the first duplicate in forwards
+ // order.
+ IndexedDBKey last_duplicate_key;
+
+ bool forward = cursor_options_.forward;
+
+ for (;;) {
+ if (next_state == SEEK) {
+ // TODO(jsbell): Optimize seeking for reverse cursors as well.
+ if (first_iteration && key && key->IsValid() && forward) {
+ iterator_->Seek(LevelDBSlice(EncodeKey(*key)));
+ first_iteration = false;
+ } else if (forward) {
+ iterator_->Next();
+ } else {
+ iterator_->Prev();
+ }
+ } else {
+ next_state = SEEK; // for subsequent iterations
+ }
+
+ if (!iterator_->IsValid()) {
+ if (!forward && last_duplicate_key.IsValid()) {
+ // We need to walk forward because we hit the end of
+ // the data.
+ forward = true;
+ continue;
+ }
+
+ return false;
+ }
+
+ if (IsPastBounds()) {
+ if (!forward && last_duplicate_key.IsValid()) {
+ // We need to walk forward because now we're beyond the
+ // bounds defined by the cursor.
+ forward = true;
+ continue;
+ }
+
+ return false;
+ }
+
+ if (!HaveEnteredRange())
+ continue;
+
+ // The row may not load because there's a stale entry in the
+ // index. This is not fatal.
+ if (!LoadCurrentRow())
+ continue;
+
+ if (key && key->IsValid()) {
+ if (forward) {
+ if (current_key_->IsLessThan(*key))
+ continue;
+ } else {
+ if (key->IsLessThan(*current_key_))
+ continue;
+ }
+ }
+
+ if (cursor_options_.unique) {
+ if (previous_key.IsValid() && current_key_->IsEqual(previous_key)) {
+ // We should never be able to walk forward all the way
+ // to the previous key.
+ DCHECK(!last_duplicate_key.IsValid());
+ continue;
+ }
+
+ if (!forward) {
+ if (!last_duplicate_key.IsValid()) {
+ last_duplicate_key = *current_key_;
+ continue;
+ }
+
+ // We need to walk forward because we hit the boundary
+ // between key ranges.
+ if (!last_duplicate_key.IsEqual(*current_key_)) {
+ forward = true;
+ continue;
+ }
+
+ continue;
+ }
+ }
+ break;
+ }
+
+ DCHECK(!last_duplicate_key.IsValid() ||
+ (forward && last_duplicate_key.IsEqual(*current_key_)));
+ return true;
+}
+
+bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
+ if (cursor_options_.forward) {
+ int compare = CompareIndexKeys(iterator_->Key(),
+ LevelDBSlice(cursor_options_.low_key));
+ if (cursor_options_.low_open) {
+ return compare > 0;
+ }
+ return compare >= 0;
+ }
+ int compare = CompareIndexKeys(iterator_->Key(),
+ LevelDBSlice(cursor_options_.high_key));
+ if (cursor_options_.high_open) {
+ return compare < 0;
+ }
+ return compare <= 0;
+}
+
+bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
+ if (cursor_options_.forward) {
+ int compare = CompareIndexKeys(iterator_->Key(),
+ LevelDBSlice(cursor_options_.high_key));
+ if (cursor_options_.high_open) {
+ return compare >= 0;
+ }
+ return compare > 0;
+ }
+ int compare =
+ CompareIndexKeys(iterator_->Key(), LevelDBSlice(cursor_options_.low_key));
+ if (cursor_options_.low_open) {
+ return compare <= 0;
+ }
+ return compare < 0;
+}
+
+const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
+ return *current_key_;
+}
+
+const IndexedDBBackingStore::RecordIdentifier&
+IndexedDBBackingStore::Cursor::record_identifier() const {
+ return record_identifier_;
+}
+
+class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
+ public:
+ ObjectStoreKeyCursorImpl(
+ LevelDBTransaction* transaction,
+ const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
+ : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+
+ virtual Cursor* Clone() OVERRIDE {
+ return new ObjectStoreKeyCursorImpl(this);
+ }
+
+ // IndexedDBBackingStore::Cursor
+ virtual std::vector<char>* Value() OVERRIDE {
+ NOTREACHED();
+ return NULL;
+ }
+ virtual bool LoadCurrentRow() OVERRIDE;
+
+ protected:
+ virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE {
+ return ObjectStoreDataKey::Encode(
+ cursor_options_.database_id, cursor_options_.object_store_id, key);
+ }
+
+ private:
+ explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
+ : IndexedDBBackingStore::Cursor(other) {}
+};
+
+bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
+ const char* key_position = iterator_->Key().begin();
+ const char* key_limit = iterator_->Key().end();
+
+ ObjectStoreDataKey object_store_data_key;
+ key_position = ObjectStoreDataKey::Decode(
+ key_position, key_limit, &object_store_data_key);
+ if (!key_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ current_key_ = object_store_data_key.user_key();
+
+ int64 version;
+ const char* value_position = DecodeVarInt(
+ iterator_->Value().begin(), iterator_->Value().end(), version);
+ if (!value_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
+ record_identifier_.Reset(EncodeIDBKey(*current_key_), version);
+
+ return true;
+}
+
+class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
+ public:
+ ObjectStoreCursorImpl(
+ LevelDBTransaction* transaction,
+ const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
+ : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+
+ virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
+
+ // IndexedDBBackingStore::Cursor
+ virtual std::vector<char>* Value() OVERRIDE { return &current_value_; }
+ virtual bool LoadCurrentRow() OVERRIDE;
+
+ protected:
+ virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE {
+ return ObjectStoreDataKey::Encode(
+ cursor_options_.database_id, cursor_options_.object_store_id, key);
+ }
+
+ private:
+ explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
+ : IndexedDBBackingStore::Cursor(other),
+ current_value_(other->current_value_) {}
+
+ std::vector<char> current_value_;
+};
+
+bool ObjectStoreCursorImpl::LoadCurrentRow() {
+ const char* key_position = iterator_->Key().begin();
+ const char* key_limit = iterator_->Key().end();
+
+ ObjectStoreDataKey object_store_data_key;
+ key_position = ObjectStoreDataKey::Decode(
+ key_position, key_limit, &object_store_data_key);
+ if (!key_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ current_key_ = object_store_data_key.user_key();
+
+ int64 version;
+ const char* value_position = DecodeVarInt(
+ iterator_->Value().begin(), iterator_->Value().end(), version);
+ if (!value_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
+ record_identifier_.Reset(EncodeIDBKey(*current_key_), version);
+
+ std::vector<char> value;
+ value.insert(value.end(), value_position, iterator_->Value().end());
+ current_value_.swap(value);
+ return true;
+}
+
+class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
+ public:
+ IndexKeyCursorImpl(
+ LevelDBTransaction* transaction,
+ const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
+ : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+
+ virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
+
+ // IndexedDBBackingStore::Cursor
+ virtual std::vector<char>* Value() OVERRIDE {
+ NOTREACHED();
+ return NULL;
+ }
+ virtual const IndexedDBKey& primary_key() const OVERRIDE {
+ return *primary_key_;
+ }
+ virtual const IndexedDBBackingStore::RecordIdentifier& RecordIdentifier()
+ const {
+ NOTREACHED();
+ return record_identifier_;
+ }
+ virtual bool LoadCurrentRow() OVERRIDE;
+
+ protected:
+ virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE {
+ return IndexDataKey::Encode(cursor_options_.database_id,
+ cursor_options_.object_store_id,
+ cursor_options_.index_id,
+ key);
+ }
+
+ private:
+ explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
+ : IndexedDBBackingStore::Cursor(other),
+ primary_key_(new IndexedDBKey(*other->primary_key_)) {}
+
+ scoped_ptr<IndexedDBKey> primary_key_;
+};
+
+bool IndexKeyCursorImpl::LoadCurrentRow() {
+ const char* key_position = iterator_->Key().begin();
+ const char* key_limit = iterator_->Key().end();
+
+ IndexDataKey index_data_key;
+ key_position = IndexDataKey::Decode(key_position, key_limit, &index_data_key);
+
+ current_key_ = index_data_key.user_key();
+ DCHECK(current_key_);
+
+ int64 index_data_version;
+ const char* value_position = DecodeVarInt(
+ iterator_->Value().begin(), iterator_->Value().end(), index_data_version);
+ if (!value_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ value_position =
+ DecodeIDBKey(value_position, iterator_->Value().end(), &primary_key_);
+ if (!value_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ std::vector<char> primary_leveldb_key =
+ ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
+ index_data_key.ObjectStoreId(),
+ *primary_key_);
+
+ std::vector<char> result;
+ bool found = false;
+ bool ok = transaction_->Get(LevelDBSlice(primary_leveldb_key), result, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+ if (!found) {
+ transaction_->Remove(iterator_->Key());
+ return false;
+ }
+ if (!result.size()) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ int64 object_store_data_version;
+ const char* t = DecodeVarInt(
+ &*result.begin(), &*result.rbegin() + 1, object_store_data_version);
+ if (!t) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ if (object_store_data_version != index_data_version) {
+ transaction_->Remove(iterator_->Key());
+ return false;
+ }
+
+ return true;
+}
+
+class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
+ public:
+ IndexCursorImpl(
+ LevelDBTransaction* transaction,
+ const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
+ : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
+
+ virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
+
+ // IndexedDBBackingStore::Cursor
+ virtual std::vector<char>* Value() OVERRIDE { return &current_value_; }
+ virtual const IndexedDBKey& primary_key() const OVERRIDE {
+ return *primary_key_;
+ }
+ virtual const IndexedDBBackingStore::RecordIdentifier& RecordIdentifier()
+ const {
+ NOTREACHED();
+ return record_identifier_;
+ }
+ virtual bool LoadCurrentRow() OVERRIDE;
+
+ protected:
+ virtual std::vector<char> EncodeKey(const IndexedDBKey& key) OVERRIDE {
+ return IndexDataKey::Encode(cursor_options_.database_id,
+ cursor_options_.object_store_id,
+ cursor_options_.index_id,
+ key);
+ }
+
+ private:
+ explicit IndexCursorImpl(const IndexCursorImpl* other)
+ : IndexedDBBackingStore::Cursor(other),
+ primary_key_(new IndexedDBKey(*other->primary_key_)),
+ current_value_(other->current_value_),
+ primary_leveldb_key_(other->primary_leveldb_key_) {}
+
+ scoped_ptr<IndexedDBKey> primary_key_;
+ std::vector<char> current_value_;
+ std::vector<char> primary_leveldb_key_;
+};
+
+bool IndexCursorImpl::LoadCurrentRow() {
+ const char* key_position = iterator_->Key().begin();
+ const char* key_limit = iterator_->Key().end();
+
+ IndexDataKey index_data_key;
+ key_position = IndexDataKey::Decode(key_position, key_limit, &index_data_key);
+
+ current_key_ = index_data_key.user_key();
+ DCHECK(current_key_);
+
+ const char* value_position = iterator_->Value().begin();
+ const char* value_limit = iterator_->Value().end();
+
+ int64 index_data_version;
+ value_position =
+ DecodeVarInt(value_position, value_limit, index_data_version);
+ if (!value_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+ value_position = DecodeIDBKey(value_position, value_limit, &primary_key_);
+ if (!value_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ primary_leveldb_key_ =
+ ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
+ index_data_key.ObjectStoreId(),
+ *primary_key_);
+
+ std::vector<char> result;
+ bool found = false;
+ bool ok =
+ transaction_->Get(LevelDBSlice(primary_leveldb_key_), result, found);
+ if (!ok) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+ if (!found) {
+ transaction_->Remove(iterator_->Key());
+ return false;
+ }
+ if (!result.size()) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ int64 object_store_data_version;
+ value_position = DecodeVarInt(
+ &*result.begin(), &*result.rbegin() + 1, object_store_data_version);
+ if (!value_position) {
+ INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
+ return false;
+ }
+
+ if (object_store_data_version != index_data_version) {
+ transaction_->Remove(iterator_->Key());
+ return false;
+ }
+
+ // TODO(jsbell): Make value_position an iterator.
+ std::vector<char> value;
+ value.insert(value.end(),
+ value_position,
+ static_cast<const char*>(&*result.rbegin()) + 1);
+ current_value_.swap(value);
+ return true;
+}
+
+bool ObjectStoreCursorOptions(
+ LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKeyRange& range,
+ indexed_db::CursorDirection direction,
+ IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) {
+ cursor_options.database_id = database_id;
+ cursor_options.object_store_id = object_store_id;
+
+ bool lower_bound = range.lower().IsValid();
+ bool upper_bound = range.upper().IsValid();
+ cursor_options.forward = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
+ direction == indexed_db::CURSOR_NEXT);
+ cursor_options.unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
+ direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
+
+ if (!lower_bound) {
+ cursor_options.low_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
+ cursor_options.low_open = true; // Not included.
+ } else {
+ cursor_options.low_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
+ cursor_options.low_open = range.lowerOpen();
+ }
+
+ if (!upper_bound) {
+ cursor_options.high_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
+
+ if (cursor_options.forward) {
+ cursor_options.high_open = true; // Not included.
+ } else {
+ // We need a key that exists.
+ if (!FindGreatestKeyLessThanOrEqual(
+ transaction, cursor_options.high_key, cursor_options.high_key))
+ return false;
+ cursor_options.high_open = false;
+ }
+ } else {
+ cursor_options.high_key =
+ ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
+ cursor_options.high_open = range.upperOpen();
+
+ if (!cursor_options.forward) {
+ // For reverse cursors, we need a key that exists.
+ std::vector<char> found_high_key;
+ if (!FindGreatestKeyLessThanOrEqual(
+ transaction, cursor_options.high_key, found_high_key))
+ return false;
+
+ // If the target key should not be included, but we end up with a smaller
+ // key, we should include that.
+ if (cursor_options.high_open &&
+ CompareIndexKeys(LevelDBSlice(found_high_key),
+ LevelDBSlice(cursor_options.high_key)) <
+ 0)
+ cursor_options.high_open = false;
+
+ cursor_options.high_key = found_high_key;
+ }
+ }
+
+ return true;
+}
+
+bool IndexCursorOptions(
+ LevelDBTransaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKeyRange& range,
+ indexed_db::CursorDirection direction,
+ IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) {
+ DCHECK(transaction);
+ if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
+ return false;
+
+ cursor_options.database_id = database_id;
+ cursor_options.object_store_id = object_store_id;
+ cursor_options.index_id = index_id;
+
+ bool lower_bound = range.lower().IsValid();
+ bool upper_bound = range.upper().IsValid();
+ cursor_options.forward = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
+ direction == indexed_db::CURSOR_NEXT);
+ cursor_options.unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
+ direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
+
+ if (!lower_bound) {
+ cursor_options.low_key =
+ IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
+ cursor_options.low_open = false; // Included.
+ } else {
+ cursor_options.low_key = IndexDataKey::Encode(
+ database_id, object_store_id, index_id, range.lower());
+ cursor_options.low_open = range.lowerOpen();
+ }
+
+ if (!upper_bound) {
+ cursor_options.high_key =
+ IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
+ cursor_options.high_open = false; // Included.
+
+ if (!cursor_options.forward) { // We need a key that exists.
+ if (!FindGreatestKeyLessThanOrEqual(
+ transaction, cursor_options.high_key, cursor_options.high_key))
+ return false;
+ cursor_options.high_open = false;
+ }
+ } else {
+ cursor_options.high_key = IndexDataKey::Encode(
+ database_id, object_store_id, index_id, range.upper());
+ cursor_options.high_open = range.upperOpen();
+
+ std::vector<char> found_high_key;
+ if (!FindGreatestKeyLessThanOrEqual(
+ transaction,
+ cursor_options.high_key,
+ found_high_key)) // Seek to the *last* key in the set of non-unique
+ // keys.
+ return false;
+
+ // If the target key should not be included, but we end up with a smaller
+ // key, we should include that.
+ if (cursor_options.high_open &&
+ CompareIndexKeys(LevelDBSlice(found_high_key),
+ LevelDBSlice(cursor_options.high_key)) <
+ 0)
+ cursor_options.high_open = false;
+
+ cursor_options.high_key = found_high_key;
+ }
+
+ return true;
+}
+
+scoped_ptr<IndexedDBBackingStore::Cursor>
+IndexedDBBackingStore::OpenObjectStoreCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKeyRange& range,
+ indexed_db::CursorDirection direction) {
+ IDB_TRACE("IndexedDBBackingStore::open_object_store_cursor");
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
+ if (!ObjectStoreCursorOptions(leveldb_transaction,
+ database_id,
+ object_store_id,
+ range,
+ direction,
+ cursor_options))
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+ scoped_ptr<ObjectStoreCursorImpl> cursor(
+ new ObjectStoreCursorImpl(leveldb_transaction, cursor_options));
+ if (!cursor->FirstSeek())
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+
+ return cursor.PassAs<IndexedDBBackingStore::Cursor>();
+}
+
+scoped_ptr<IndexedDBBackingStore::Cursor>
+IndexedDBBackingStore::OpenObjectStoreKeyCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKeyRange& range,
+ indexed_db::CursorDirection direction) {
+ IDB_TRACE("IndexedDBBackingStore::open_object_store_key_cursor");
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
+ if (!ObjectStoreCursorOptions(leveldb_transaction,
+ database_id,
+ object_store_id,
+ range,
+ direction,
+ cursor_options))
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+ scoped_ptr<ObjectStoreKeyCursorImpl> cursor(
+ new ObjectStoreKeyCursorImpl(leveldb_transaction, cursor_options));
+ if (!cursor->FirstSeek())
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+
+ return cursor.PassAs<IndexedDBBackingStore::Cursor>();
+}
+
+scoped_ptr<IndexedDBBackingStore::Cursor>
+IndexedDBBackingStore::OpenIndexKeyCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKeyRange& range,
+ indexed_db::CursorDirection direction) {
+ IDB_TRACE("IndexedDBBackingStore::open_index_key_cursor");
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
+ if (!IndexCursorOptions(leveldb_transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ range,
+ direction,
+ cursor_options))
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+ scoped_ptr<IndexKeyCursorImpl> cursor(
+ new IndexKeyCursorImpl(leveldb_transaction, cursor_options));
+ if (!cursor->FirstSeek())
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+
+ return cursor.PassAs<IndexedDBBackingStore::Cursor>();
+}
+
+scoped_ptr<IndexedDBBackingStore::Cursor>
+IndexedDBBackingStore::OpenIndexCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKeyRange& range,
+ indexed_db::CursorDirection direction) {
+ IDB_TRACE("IndexedDBBackingStore::open_index_cursor");
+ LevelDBTransaction* leveldb_transaction =
+ IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
+ IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
+ if (!IndexCursorOptions(leveldb_transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ range,
+ direction,
+ cursor_options))
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+ scoped_ptr<IndexCursorImpl> cursor(
+ new IndexCursorImpl(leveldb_transaction, cursor_options));
+ if (!cursor->FirstSeek())
+ return scoped_ptr<IndexedDBBackingStore::Cursor>();
+
+ return cursor.PassAs<IndexedDBBackingStore::Cursor>();
+}
+
+IndexedDBBackingStore::Transaction::Transaction(
+ IndexedDBBackingStore* backing_store)
+ : backing_store_(backing_store) {}
+
+IndexedDBBackingStore::Transaction::~Transaction() {}
+
+void IndexedDBBackingStore::Transaction::begin() {
+ IDB_TRACE("IndexedDBBackingStore::Transaction::begin");
+ DCHECK(!transaction_);
+ transaction_ = LevelDBTransaction::Create(backing_store_->db_.get());
+}
+
+bool IndexedDBBackingStore::Transaction::Commit() {
+ IDB_TRACE("IndexedDBBackingStore::Transaction::commit");
+ DCHECK(transaction_);
+ bool result = transaction_->Commit();
+ transaction_ = NULL;
+ if (!result)
+ INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
+ return result;
+}
+
+void IndexedDBBackingStore::Transaction::Rollback() {
+ IDB_TRACE("IndexedDBBackingStore::Transaction::rollback");
+ DCHECK(transaction_);
+ transaction_->Rollback();
+ transaction_ = NULL;
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
new file mode 100644
index 0000000..6862d91
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -0,0 +1,319 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_BACKING_STORE_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_BACKING_STORE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/indexed_db/indexed_db.h"
+#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
+#include "content/common/content_export.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+
+namespace content {
+
+class LevelDBComparator;
+class LevelDBDatabase;
+
+class LevelDBFactory {
+ public:
+ virtual scoped_ptr<LevelDBDatabase> OpenLevelDB(
+ const base::FilePath& file_name,
+ const LevelDBComparator* comparator) = 0;
+ virtual bool DestroyLevelDB(const base::FilePath& file_name) = 0;
+};
+
+class CONTENT_EXPORT IndexedDBBackingStore
+ : public base::RefCounted<IndexedDBBackingStore> {
+ public:
+ class CONTENT_EXPORT Transaction;
+
+ static scoped_refptr<IndexedDBBackingStore> Open(
+ const string16& database_identifier,
+ const string16& path_base,
+ const string16& file_identifier);
+ static scoped_refptr<IndexedDBBackingStore> Open(
+ const string16& database_identifier,
+ const string16& path_base,
+ const string16& file_identifier,
+ LevelDBFactory* factory);
+ static scoped_refptr<IndexedDBBackingStore> OpenInMemory(
+ const string16& identifier);
+ static scoped_refptr<IndexedDBBackingStore> OpenInMemory(
+ const string16& identifier,
+ LevelDBFactory* factory);
+ base::WeakPtr<IndexedDBBackingStore> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ virtual std::vector<string16> GetDatabaseNames();
+ virtual bool GetIDBDatabaseMetaData(const string16& name,
+ IndexedDBDatabaseMetadata* metadata,
+ bool& success) WARN_UNUSED_RESULT;
+ virtual bool CreateIDBDatabaseMetaData(const string16& name,
+ const string16& version,
+ int64 int_version,
+ int64& row_id);
+ virtual bool UpdateIDBDatabaseMetaData(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 row_id,
+ const string16& version);
+ virtual bool UpdateIDBDatabaseIntVersion(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 row_id,
+ int64 int_version);
+ virtual bool DeleteDatabase(const string16& name);
+
+ bool GetObjectStores(int64 database_id,
+ IndexedDBDatabaseMetadata::ObjectStoreMap* map)
+ WARN_UNUSED_RESULT;
+ virtual bool CreateObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment);
+ virtual bool DeleteObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) WARN_UNUSED_RESULT;
+
+ class CONTENT_EXPORT RecordIdentifier {
+ public:
+ RecordIdentifier(const std::vector<char>& primary_key, int64 version);
+ RecordIdentifier();
+ ~RecordIdentifier();
+
+ const std::vector<char>& primary_key() const { return primary_key_; }
+ int64 version() const { return version_; }
+ void Reset(const std::vector<char>& primary_key, int64 version) {
+ primary_key_ = primary_key;
+ version_ = version;
+ }
+
+ private:
+ std::vector<char>
+ primary_key_; // TODO(jsbell): Make it more clear that this is
+ // the *encoded* version of the key.
+ int64 version_;
+ DISALLOW_COPY_AND_ASSIGN(RecordIdentifier);
+ };
+
+ virtual bool GetRecord(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ std::vector<char>& record) WARN_UNUSED_RESULT;
+ virtual bool PutRecord(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ const std::vector<char>& value,
+ RecordIdentifier* record) WARN_UNUSED_RESULT;
+ virtual bool ClearObjectStore(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) WARN_UNUSED_RESULT;
+ virtual bool DeleteRecord(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const RecordIdentifier& record) WARN_UNUSED_RESULT;
+ virtual bool GetKeyGeneratorCurrentNumber(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64& current_number) WARN_UNUSED_RESULT;
+ virtual bool MaybeUpdateKeyGeneratorCurrentNumber(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 new_state,
+ bool check_current) WARN_UNUSED_RESULT;
+ virtual bool KeyExistsInObjectStore(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& key,
+ RecordIdentifier* found_record_identifier,
+ bool& found) WARN_UNUSED_RESULT;
+
+ virtual bool CreateIndex(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool is_unique,
+ bool is_multi_entry) WARN_UNUSED_RESULT;
+ virtual bool DeleteIndex(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id) WARN_UNUSED_RESULT;
+ virtual bool PutIndexDataForRecord(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ const RecordIdentifier& record) WARN_UNUSED_RESULT;
+ virtual bool GetPrimaryKeyViaIndex(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ scoped_ptr<IndexedDBKey>* primary_key) WARN_UNUSED_RESULT;
+ virtual bool KeyExistsInIndex(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ scoped_ptr<IndexedDBKey>* found_primary_key,
+ bool& exists) WARN_UNUSED_RESULT;
+
+ class Cursor {
+ public:
+ virtual ~Cursor();
+
+ enum IteratorState {
+ READY = 0,
+ SEEK
+ };
+
+ struct CursorOptions {
+ CursorOptions();
+ ~CursorOptions();
+ int64 database_id;
+ int64 object_store_id;
+ int64 index_id;
+ std::vector<char> low_key;
+ bool low_open;
+ std::vector<char> high_key;
+ bool high_open;
+ bool forward;
+ bool unique;
+ };
+
+ const IndexedDBKey& key() const { return *current_key_; }
+ bool ContinueFunction(const IndexedDBKey* = 0, IteratorState = SEEK);
+ bool Advance(unsigned long);
+ bool FirstSeek();
+
+ virtual Cursor* Clone() = 0;
+ virtual const IndexedDBKey& primary_key() const;
+ virtual std::vector<char>* Value() = 0;
+ virtual const RecordIdentifier& record_identifier() const;
+ virtual bool LoadCurrentRow() = 0;
+
+ protected:
+ Cursor(LevelDBTransaction* transaction,
+ const CursorOptions& cursor_options);
+ explicit Cursor(const IndexedDBBackingStore::Cursor* other);
+
+ virtual std::vector<char> EncodeKey(const IndexedDBKey& key) = 0;
+
+ bool IsPastBounds() const;
+ bool HaveEnteredRange() const;
+
+ LevelDBTransaction* transaction_;
+ const CursorOptions cursor_options_;
+ scoped_ptr<LevelDBIterator> iterator_;
+ scoped_ptr<IndexedDBKey> current_key_;
+ IndexedDBBackingStore::RecordIdentifier record_identifier_;
+ };
+
+ virtual scoped_ptr<Cursor> OpenObjectStoreKeyCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKeyRange& key_range,
+ indexed_db::CursorDirection);
+ virtual scoped_ptr<Cursor> OpenObjectStoreCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKeyRange& key_range,
+ indexed_db::CursorDirection);
+ virtual scoped_ptr<Cursor> OpenIndexKeyCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKeyRange& key_range,
+ indexed_db::CursorDirection);
+ virtual scoped_ptr<Cursor> OpenIndexCursor(
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKeyRange& key_range,
+ indexed_db::CursorDirection);
+
+ class Transaction {
+ public:
+ explicit Transaction(IndexedDBBackingStore* backing_store);
+ ~Transaction();
+ void begin();
+ bool Commit();
+ void Rollback();
+ void Reset() {
+ backing_store_ = NULL;
+ transaction_ = NULL;
+ }
+
+ static LevelDBTransaction* LevelDBTransactionFrom(
+ Transaction* transaction) {
+ return transaction->transaction_.get();
+ }
+
+ private:
+ IndexedDBBackingStore* backing_store_;
+ scoped_refptr<LevelDBTransaction> transaction_;
+ };
+
+ protected:
+ IndexedDBBackingStore(const string16& identifier,
+ scoped_ptr<LevelDBDatabase> db,
+ scoped_ptr<LevelDBComparator> comparator);
+ virtual ~IndexedDBBackingStore();
+ friend class base::RefCounted<IndexedDBBackingStore>;
+
+ private:
+ static scoped_refptr<IndexedDBBackingStore> Create(
+ const string16& identifier,
+ scoped_ptr<LevelDBDatabase> db,
+ scoped_ptr<LevelDBComparator> comparator);
+
+ bool FindKeyInIndex(IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& key,
+ std::vector<char>& found_encoded_primary_key,
+ bool& found);
+ bool GetIndexes(int64 database_id,
+ int64 object_store_id,
+ IndexedDBObjectStoreMetadata::IndexMap* map)
+ WARN_UNUSED_RESULT;
+
+ string16 identifier_;
+
+ scoped_ptr<LevelDBDatabase> db_;
+ scoped_ptr<LevelDBComparator> comparator_;
+ base::WeakPtrFactory<IndexedDBBackingStore> weak_factory_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_BACKING_STORE_H_
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
new file mode 100644
index 0000000..0e60d7b
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -0,0 +1,403 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_factory_impl.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
+#include "third_party/WebKit/public/platform/WebData.h"
+
+using WebKit::WebSecurityOrigin;
+using WebKit::WebIDBKey;
+
+namespace content {
+
+namespace {
+
+class IndexedDBBackingStoreTest : public testing::Test {
+ public:
+ IndexedDBBackingStoreTest() {}
+ virtual void SetUp() {
+ string16 file_identifier;
+ backing_store_ = IndexedDBBackingStore::OpenInMemory(file_identifier);
+
+ // useful keys and values during tests
+ const char raw_value1[] = "value1";
+
+ const char raw_value2[] = "value2";
+ const char raw_value3[] = "value3";
+ m_value1.insert(
+ m_value1.end(), &raw_value1[0], &raw_value1[0] + sizeof(raw_value1));
+ m_value2.insert(
+ m_value2.end(), &raw_value2[0], &raw_value2[0] + sizeof(raw_value2));
+ m_value3.insert(
+ m_value3.end(), &raw_value3[0], &raw_value3[0] + sizeof(raw_value3));
+ m_key1 = IndexedDBKey(99, WebIDBKey::NumberType);
+ m_key2 = IndexedDBKey(ASCIIToUTF16("key2"));
+ m_key3 = IndexedDBKey(ASCIIToUTF16("key3"));
+ }
+
+ protected:
+ scoped_refptr<IndexedDBBackingStore> backing_store_;
+
+ // Sample keys and values that are consistent.
+ IndexedDBKey m_key1;
+ IndexedDBKey m_key2;
+ IndexedDBKey m_key3;
+ std::vector<char> m_value1;
+ std::vector<char> m_value2;
+ std::vector<char> m_value3;
+};
+
+TEST_F(IndexedDBBackingStoreTest, PutGetConsistency) {
+ {
+ IndexedDBBackingStore::Transaction transaction1(backing_store_.get());
+ transaction1.begin();
+ IndexedDBBackingStore::RecordIdentifier record;
+ bool ok = backing_store_->PutRecord(
+ &transaction1, 1, 1, m_key1, m_value1, &record);
+ EXPECT_TRUE(ok);
+ transaction1.Commit();
+ }
+
+ {
+ IndexedDBBackingStore::Transaction transaction2(backing_store_.get());
+ transaction2.begin();
+ std::vector<char> result_value;
+ bool ok =
+ backing_store_->GetRecord(&transaction2, 1, 1, m_key1, result_value);
+ transaction2.Commit();
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(m_value1, result_value);
+ }
+}
+
+// Make sure that using very high ( more than 32 bit ) values for database_id
+// and object_store_id still work.
+TEST_F(IndexedDBBackingStoreTest, HighIds) {
+ const int64 high_database_id = 1ULL << 35;
+ const int64 high_object_store_id = 1ULL << 39;
+ // index_ids are capped at 32 bits for storage purposes.
+ const int64 high_index_id = 1ULL << 29;
+
+ const int64 invalid_high_index_id = 1ULL << 37;
+
+ const IndexedDBKey& index_key = m_key2;
+ std::vector<char> index_key_raw = EncodeIDBKey(index_key);
+ {
+ IndexedDBBackingStore::Transaction transaction1(backing_store_.get());
+ transaction1.begin();
+ IndexedDBBackingStore::RecordIdentifier record;
+ bool ok = backing_store_->PutRecord(&transaction1,
+ high_database_id,
+ high_object_store_id,
+ m_key1,
+ m_value1,
+ &record);
+ EXPECT_TRUE(ok);
+
+ ok = backing_store_->PutIndexDataForRecord(&transaction1,
+ high_database_id,
+ high_object_store_id,
+ invalid_high_index_id,
+ index_key,
+ record);
+ EXPECT_FALSE(ok);
+
+ ok = backing_store_->PutIndexDataForRecord(&transaction1,
+ high_database_id,
+ high_object_store_id,
+ high_index_id,
+ index_key,
+ record);
+ EXPECT_TRUE(ok);
+
+ ok = transaction1.Commit();
+ EXPECT_TRUE(ok);
+ }
+
+ {
+ IndexedDBBackingStore::Transaction transaction2(backing_store_.get());
+ transaction2.begin();
+ std::vector<char> result_value;
+ bool ok = backing_store_->GetRecord(&transaction2,
+ high_database_id,
+ high_object_store_id,
+ m_key1,
+ result_value);
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(m_value1, result_value);
+
+ scoped_ptr<IndexedDBKey> new_primary_key;
+ ok = backing_store_->GetPrimaryKeyViaIndex(&transaction2,
+ high_database_id,
+ high_object_store_id,
+ invalid_high_index_id,
+ index_key,
+ &new_primary_key);
+ EXPECT_FALSE(ok);
+
+ ok = backing_store_->GetPrimaryKeyViaIndex(&transaction2,
+ high_database_id,
+ high_object_store_id,
+ high_index_id,
+ index_key,
+ &new_primary_key);
+ EXPECT_TRUE(ok);
+ EXPECT_TRUE(new_primary_key->IsEqual(m_key1));
+
+ ok = transaction2.Commit();
+ EXPECT_TRUE(ok);
+ }
+}
+
+// Make sure that other invalid ids do not crash.
+TEST_F(IndexedDBBackingStoreTest, InvalidIds) {
+ // valid ids for use when testing invalid ids
+ const int64 database_id = 1;
+ const int64 object_store_id = 1;
+ const int64 index_id = kMinimumIndexId;
+ const int64 invalid_low_index_id = 19; // index_ids must be > kMinimumIndexId
+
+ std::vector<char> result_value;
+
+ IndexedDBBackingStore::Transaction transaction1(backing_store_.get());
+ transaction1.begin();
+
+ IndexedDBBackingStore::RecordIdentifier record;
+ bool ok = backing_store_->PutRecord(&transaction1,
+ database_id,
+ KeyPrefix::kInvalidId,
+ m_key1,
+ m_value1,
+ &record);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->PutRecord(
+ &transaction1, database_id, 0, m_key1, m_value1, &record);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->PutRecord(&transaction1,
+ KeyPrefix::kInvalidId,
+ object_store_id,
+ m_key1,
+ m_value1,
+ &record);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->PutRecord(
+ &transaction1, 0, object_store_id, m_key1, m_value1, &record);
+ EXPECT_FALSE(ok);
+
+ ok = backing_store_->GetRecord(
+ &transaction1, database_id, KeyPrefix::kInvalidId, m_key1, result_value);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->GetRecord(
+ &transaction1, database_id, 0, m_key1, result_value);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->GetRecord(&transaction1,
+ KeyPrefix::kInvalidId,
+ object_store_id,
+ m_key1,
+ result_value);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->GetRecord(
+ &transaction1, 0, object_store_id, m_key1, result_value);
+ EXPECT_FALSE(ok);
+
+ scoped_ptr<IndexedDBKey> new_primary_key;
+ ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ database_id,
+ object_store_id,
+ KeyPrefix::kInvalidId,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ database_id,
+ object_store_id,
+ invalid_low_index_id,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->GetPrimaryKeyViaIndex(
+ &transaction1, database_id, object_store_id, 0, m_key1, &new_primary_key);
+ EXPECT_FALSE(ok);
+
+ ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ KeyPrefix::kInvalidId,
+ object_store_id,
+ index_id,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(ok);
+ ok = backing_store_->GetPrimaryKeyViaIndex(&transaction1,
+ database_id,
+ KeyPrefix::kInvalidId,
+ index_id,
+ m_key1,
+ &new_primary_key);
+ EXPECT_FALSE(ok);
+}
+
+TEST_F(IndexedDBBackingStoreTest, CreateDatabase) {
+ const string16 database_name(ASCIIToUTF16("db1"));
+ int64 database_id;
+ const string16 version(ASCIIToUTF16("old_string_version"));
+ const int64 int_version = 9;
+
+ const int64 object_store_id = 99;
+ const string16 object_store_name(ASCIIToUTF16("object_store1"));
+ const bool auto_increment = true;
+ const IndexedDBKeyPath object_store_key_path(
+ ASCIIToUTF16("object_store_key"));
+
+ const int64 index_id = 999;
+ const string16 index_name(ASCIIToUTF16("index1"));
+ const bool unique = true;
+ const bool multi_entry = true;
+ const IndexedDBKeyPath index_key_path(ASCIIToUTF16("index_key"));
+
+ {
+ bool ok = backing_store_->CreateIDBDatabaseMetaData(
+ database_name, version, int_version, database_id);
+ EXPECT_TRUE(ok);
+ EXPECT_GT(database_id, 0);
+
+ IndexedDBBackingStore::Transaction transaction(backing_store_.get());
+ transaction.begin();
+
+ ok = backing_store_->CreateObjectStore(&transaction,
+ database_id,
+ object_store_id,
+ object_store_name,
+ object_store_key_path,
+ auto_increment);
+ EXPECT_TRUE(ok);
+
+ ok = backing_store_->CreateIndex(&transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_name,
+ index_key_path,
+ unique,
+ multi_entry);
+ EXPECT_TRUE(ok);
+
+ ok = transaction.Commit();
+ EXPECT_TRUE(ok);
+ }
+
+ {
+ IndexedDBDatabaseMetadata database;
+ bool found;
+ bool ok =
+ backing_store_->GetIDBDatabaseMetaData(database_name, &database, found);
+ EXPECT_TRUE(ok);
+ EXPECT_TRUE(found);
+
+ // database.name is not filled in by the implementation.
+ EXPECT_EQ(version, database.version);
+ EXPECT_EQ(int_version, database.int_version);
+ EXPECT_EQ(database_id, database.id);
+
+ ok = backing_store_->GetObjectStores(database.id, &database.object_stores);
+ EXPECT_TRUE(ok);
+
+ EXPECT_EQ(1UL, database.object_stores.size());
+ IndexedDBObjectStoreMetadata object_store =
+ database.object_stores[object_store_id];
+ EXPECT_EQ(object_store_name, object_store.name);
+ EXPECT_EQ(object_store_key_path, object_store.key_path);
+ EXPECT_EQ(auto_increment, object_store.auto_increment);
+
+ EXPECT_EQ(1UL, object_store.indexes.size());
+ IndexedDBIndexMetadata index = object_store.indexes[index_id];
+ EXPECT_EQ(index_name, index.name);
+ EXPECT_EQ(index_key_path, index.key_path);
+ EXPECT_EQ(unique, index.unique);
+ EXPECT_EQ(multi_entry, index.multi_entry);
+ }
+}
+
+class MockIDBFactory : public IndexedDBFactoryImpl {
+ public:
+ static scoped_refptr<MockIDBFactory> Create() {
+ return make_scoped_refptr(new MockIDBFactory());
+ }
+
+ scoped_refptr<IndexedDBBackingStore> TestOpenBackingStore(
+ const WebSecurityOrigin& origin,
+ const base::FilePath& data_directory) {
+ string16 path = UTF8ToUTF16(data_directory.AsUTF8Unsafe());
+ return OpenBackingStore(origin.databaseIdentifier(), path);
+ }
+
+ private:
+ virtual ~MockIDBFactory() {}
+};
+
+TEST(IndexedDBFactoryTest, BackingStoreLifetime) {
+ WebSecurityOrigin origin1 =
+ WebSecurityOrigin::createFromString("http://localhost:81");
+ WebSecurityOrigin origin2 =
+ WebSecurityOrigin::createFromString("http://localhost:82");
+
+ scoped_refptr<MockIDBFactory> factory = MockIDBFactory::Create();
+
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+ scoped_refptr<IndexedDBBackingStore> disk_store1 =
+ factory->TestOpenBackingStore(origin1, temp_directory.path());
+ EXPECT_TRUE(disk_store1->HasOneRef());
+
+ scoped_refptr<IndexedDBBackingStore> disk_store2 =
+ factory->TestOpenBackingStore(origin1, temp_directory.path());
+ EXPECT_EQ(disk_store1.get(), disk_store2.get());
+ // TODO(alecflett): Verify refcount indirectly.
+ // EXPECT_EQ(2, disk_store2->RefCount());
+
+ scoped_refptr<IndexedDBBackingStore> disk_store3 =
+ factory->TestOpenBackingStore(origin2, temp_directory.path());
+ EXPECT_TRUE(disk_store3->HasOneRef());
+ // TODO(alecflett): Verify refcount indirectly.
+ // EXPECT_EQ(2, disk_store1->RefCount());
+}
+
+TEST(IndexedDBFactoryTest, MemoryBackingStoreLifetime) {
+ WebSecurityOrigin origin1 =
+ WebSecurityOrigin::createFromString("http://localhost:81");
+ WebSecurityOrigin origin2 =
+ WebSecurityOrigin::createFromString("http://localhost:82");
+
+ scoped_refptr<MockIDBFactory> factory = MockIDBFactory::Create();
+ scoped_refptr<IndexedDBBackingStore> mem_store1 =
+ factory->TestOpenBackingStore(origin1, base::FilePath());
+ // TODO(alecflett): Verify refcount indirectly.
+ // EXPECT_EQ(2, mem_store1->RefCount());
+ scoped_refptr<IndexedDBBackingStore> mem_store2 =
+ factory->TestOpenBackingStore(origin1, base::FilePath());
+ EXPECT_EQ(mem_store1.get(), mem_store2.get());
+ // TODO(alecflett): Verify refcount indirectly.
+ // EXPECT_EQ(3, mem_store2->RefCount());
+
+ scoped_refptr<IndexedDBBackingStore> mem_store3 =
+ factory->TestOpenBackingStore(origin2, base::FilePath());
+ // TODO(alecflett): Verify refcount indirectly.
+ // EXPECT_EQ(2, mem_store3->RefCount());
+ // EXPECT_EQ(3, mem_store1->RefCount());
+
+ factory = NULL;
+ // TODO(alecflett): Verify refcount indirectly.
+ // EXPECT_EQ(2, mem_store1->RefCount());
+ // EXPECT_EQ(1, mem_store3->RefCount());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_callbacks_wrapper.cc b/content/browser/indexed_db/indexed_db_callbacks_wrapper.cc
new file mode 100644
index 0000000..1c34878
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_callbacks_wrapper.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h"
+
+#include "content/browser/indexed_db/indexed_db_cursor.h"
+#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/browser/indexed_db/webidbcursor_impl.h"
+#include "content/browser/indexed_db/webidbdatabase_impl.h"
+#include "third_party/WebKit/public/platform/WebData.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseError.h"
+#include "third_party/WebKit/public/platform/WebIDBKey.h"
+#include "third_party/WebKit/public/platform/WebIDBMetadata.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+
+namespace content {
+
+using WebKit::WebData;
+using WebKit::WebIDBKey;
+using WebKit::WebIDBMetadata;
+using WebKit::WebString;
+using WebKit::WebVector;
+
+IndexedDBCallbacksWrapper::IndexedDBCallbacksWrapper(
+ WebKit::WebIDBCallbacks* callbacks)
+ : callbacks_(callbacks), did_complete_(false), did_create_proxy_(false) {}
+
+IndexedDBCallbacksWrapper::~IndexedDBCallbacksWrapper() {}
+
+void IndexedDBCallbacksWrapper::OnError(
+ scoped_refptr<IndexedDBDatabaseError> error) {
+ DCHECK(callbacks_);
+ callbacks_->onError(
+ WebKit::WebIDBDatabaseError(error->code(), error->message()));
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(const std::vector<string16>& value) {
+ DCHECK(callbacks_);
+ callbacks_->onSuccess(WebVector<WebString>(value));
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(scoped_refptr<IndexedDBCursor> cursor,
+ const IndexedDBKey& key,
+ const IndexedDBKey& primary_key,
+ std::vector<char>* value) {
+ DCHECK(callbacks_);
+ WebData web_value;
+ if (value && value->size())
+ web_value.assign(&value->front(), value->size());
+ callbacks_->onSuccess(
+ new WebIDBCursorImpl(cursor), WebIDBKey(key), primary_key, web_value);
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(const IndexedDBKey& key) {
+ DCHECK(callbacks_);
+ callbacks_->onSuccess(WebIDBKey(key));
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(std::vector<char>* value) {
+ WebData web_value;
+ if (value && value->size())
+ web_value.assign(&value->front(), value->size());
+
+ DCHECK(callbacks_);
+ callbacks_->onSuccess(web_value);
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(std::vector<char>* value,
+ const IndexedDBKey& key,
+ const IndexedDBKeyPath& key_path) {
+ WebData web_value;
+ if (value && value->size())
+ web_value.assign(&value->front(), value->size());
+ DCHECK(callbacks_);
+ callbacks_->onSuccess(web_value, WebIDBKey(key), key_path);
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(int64 value) {
+ DCHECK(callbacks_);
+ callbacks_->onSuccess(value);
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess() {
+ DCHECK(callbacks_);
+ callbacks_->onSuccess();
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(const IndexedDBKey& key,
+ const IndexedDBKey& primary_key,
+ std::vector<char>* value) {
+ WebData web_value;
+ if (value && value->size())
+ web_value.assign(&value->front(), value->size());
+ DCHECK(callbacks_);
+ callbacks_->onSuccess(key, primary_key, web_value);
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnSuccessWithPrefetch(
+ const std::vector<IndexedDBKey>& keys,
+ const std::vector<IndexedDBKey>& primary_keys,
+ const std::vector<std::vector<char> >& values) {
+ DCHECK_EQ(keys.size(), primary_keys.size());
+ DCHECK_EQ(keys.size(), values.size());
+
+ std::vector<WebIDBKey> web_keys(keys.size());
+ std::vector<WebIDBKey> web_primary_keys(primary_keys.size());
+ std::vector<WebData> web_values(values.size());
+ for (size_t i = 0; i < keys.size(); ++i) {
+ web_keys[i] = keys[i];
+ web_primary_keys[i] = primary_keys[i];
+ if (values[i].size())
+ web_values[i].assign(&values[i].front(), values[i].size());
+ }
+
+ DCHECK(callbacks_);
+ callbacks_->onSuccessWithPrefetch(web_keys, web_primary_keys, web_values);
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::OnBlocked(int64 existing_version) {
+ DCHECK(callbacks_);
+ callbacks_->onBlocked(existing_version);
+}
+
+WebIDBMetadata ConvertMetadata(const IndexedDBDatabaseMetadata& idb_metadata) {
+ WebIDBMetadata web_metadata;
+ web_metadata.id = idb_metadata.id;
+ web_metadata.name = idb_metadata.name;
+ web_metadata.version = idb_metadata.version;
+ web_metadata.intVersion = idb_metadata.int_version;
+ web_metadata.maxObjectStoreId = idb_metadata.max_object_store_id;
+ web_metadata.objectStores =
+ WebVector<WebIDBMetadata::ObjectStore>(idb_metadata.object_stores.size());
+
+ size_t i = 0;
+ for (IndexedDBDatabaseMetadata::ObjectStoreMap::const_iterator
+ it = idb_metadata.object_stores.begin();
+ it != idb_metadata.object_stores.end();
+ ++it, ++i) {
+ const IndexedDBObjectStoreMetadata& idb_store_metadata = it->second;
+ WebIDBMetadata::ObjectStore& web_store_metadata =
+ web_metadata.objectStores[i];
+
+ web_store_metadata.id = idb_store_metadata.id;
+ web_store_metadata.name = idb_store_metadata.name;
+ web_store_metadata.keyPath = idb_store_metadata.key_path;
+ web_store_metadata.autoIncrement = idb_store_metadata.auto_increment;
+ web_store_metadata.maxIndexId = idb_store_metadata.max_index_id;
+ web_store_metadata.indexes =
+ WebVector<WebIDBMetadata::Index>(idb_store_metadata.indexes.size());
+
+ size_t j = 0;
+ for (IndexedDBObjectStoreMetadata::IndexMap::const_iterator
+ it2 = idb_store_metadata.indexes.begin();
+ it2 != idb_store_metadata.indexes.end();
+ ++it2, ++j) {
+ const IndexedDBIndexMetadata& idb_index_metadata = it2->second;
+ WebIDBMetadata::Index& web_index_metadata = web_store_metadata.indexes[j];
+
+ web_index_metadata.id = idb_index_metadata.id;
+ web_index_metadata.name = idb_index_metadata.name;
+ web_index_metadata.keyPath = idb_index_metadata.key_path;
+ web_index_metadata.unique = idb_index_metadata.unique;
+ web_index_metadata.multiEntry = idb_index_metadata.multi_entry;
+ }
+ }
+
+ return web_metadata;
+}
+
+void IndexedDBCallbacksWrapper::OnUpgradeNeeded(
+ int64 old_version,
+ scoped_refptr<IndexedDBDatabase> database,
+ const IndexedDBDatabaseMetadata& metadata) {
+ DCHECK(callbacks_);
+ WebIDBMetadata web_metadata = ConvertMetadata(metadata);
+ did_create_proxy_ = true;
+ callbacks_->onUpgradeNeeded(
+ old_version,
+ new WebIDBDatabaseImpl(database, database_callbacks_),
+ web_metadata);
+ database_callbacks_ = NULL;
+}
+
+void IndexedDBCallbacksWrapper::OnSuccess(
+ scoped_refptr<IndexedDBDatabase> database,
+ const IndexedDBDatabaseMetadata& metadata) {
+ DCHECK(callbacks_);
+ WebIDBMetadata web_metadata = ConvertMetadata(metadata);
+ scoped_refptr<IndexedDBCallbacksWrapper> self(this);
+
+ WebIDBDatabaseImpl* impl =
+ did_create_proxy_ ? 0
+ : new WebIDBDatabaseImpl(database, database_callbacks_);
+ database_callbacks_ = NULL;
+
+ callbacks_->onSuccess(impl, web_metadata);
+ callbacks_.reset();
+}
+
+void IndexedDBCallbacksWrapper::SetDatabaseCallbacks(
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks) {
+ database_callbacks_ = database_callbacks;
+}
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_callbacks_wrapper.h b/content/browser/indexed_db/indexed_db_callbacks_wrapper.h
new file mode 100644
index 0000000..7fdd4d5
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_callbacks_wrapper.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CALLBACKS_WRAPPER_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CALLBACKS_WRAPPER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/indexed_db/indexed_db_database.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "third_party/WebKit/public/platform/WebIDBCallbacks.h"
+
+namespace WebKit {
+class WebIDBCallbacks;
+}
+
+namespace content {
+class IndexedDBCursor;
+class WebIDBDatabaseImpl;
+
+class IndexedDBCallbacksWrapper
+ : public base::RefCounted<IndexedDBCallbacksWrapper> {
+ public:
+ static scoped_refptr<IndexedDBCallbacksWrapper> Create(
+ WebKit::WebIDBCallbacks* callbacks) {
+ return make_scoped_refptr(new IndexedDBCallbacksWrapper(callbacks));
+ }
+
+ virtual void OnError(scoped_refptr<IndexedDBDatabaseError> error);
+ // From IDBFactory.webkitGetDatabaseNames()
+ virtual void OnSuccess(const std::vector<string16>& string);
+ // From IDBObjectStore/IDBIndex.openCursor(),
+ // IDBIndex.openKeyCursor()
+ virtual void OnSuccess(scoped_refptr<IndexedDBCursor> cursor,
+ const IndexedDBKey& key,
+ const IndexedDBKey& primary_key,
+ std::vector<char>* value);
+ // From IDBObjectStore.add()/put(), IDBIndex.getKey()
+ virtual void OnSuccess(const IndexedDBKey& key);
+ // From IDBObjectStore/IDBIndex.get()/count(), and various methods
+ // that yield null/undefined.
+ virtual void OnSuccess(std::vector<char>* value);
+ // From IDBObjectStore/IDBIndex.get() (with key injection)
+ virtual void OnSuccess(std::vector<char>* data,
+ const IndexedDBKey& key,
+ const IndexedDBKeyPath& key_path);
+ // From IDBObjectStore/IDBIndex.count()
+ virtual void OnSuccess(int64 value);
+
+ // From IDBFactor.deleteDatabase(),
+ // IDBObjectStore/IDBIndex.get(), IDBObjectStore.delete(),
+ // IDBObjectStore.clear()
+ virtual void OnSuccess();
+
+ // From IDBCursor.advance()/continue()
+ virtual void OnSuccess(const IndexedDBKey& key,
+ const IndexedDBKey& primary_key,
+ std::vector<char>* value);
+ // From IDBCursor.advance()/continue()
+ virtual void OnSuccessWithPrefetch(
+ const std::vector<IndexedDBKey>& keys,
+ const std::vector<IndexedDBKey>& primary_keys,
+ const std::vector<std::vector<char> >& values);
+ // From IDBFactory.open()/deleteDatabase()
+ virtual void OnBlocked(int64 /* existing_version */);
+ // From IDBFactory.open()
+ virtual void OnUpgradeNeeded(int64 /* old_version */,
+ scoped_refptr<IndexedDBDatabase> db,
+ const IndexedDBDatabaseMetadata& metadata);
+ virtual void OnSuccess(scoped_refptr<IndexedDBDatabase> db,
+ const IndexedDBDatabaseMetadata& metadata);
+ virtual void SetDatabaseCallbacks(
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks);
+
+ private:
+ explicit IndexedDBCallbacksWrapper(WebKit::WebIDBCallbacks* callbacks);
+ virtual ~IndexedDBCallbacksWrapper();
+ friend class base::RefCounted<IndexedDBCallbacksWrapper>;
+ scoped_ptr<WebIDBDatabaseImpl> web_database_impl_;
+ scoped_ptr<WebKit::WebIDBCallbacks> callbacks_;
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
+ bool did_complete_;
+ bool did_create_proxy_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CALLBACKS_WRAPPER_H_
diff --git a/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
new file mode 100644
index 0000000..af86147
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/leveldb/leveldb_database.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
+
+using WebKit::WebSecurityOrigin;
+using content::IndexedDBBackingStore;
+using content::LevelDBComparator;
+using content::LevelDBDatabase;
+using content::LevelDBFactory;
+using content::LevelDBSlice;
+using content::LevelDBSnapshot;
+
+namespace {
+
+class BustedLevelDBDatabase : public LevelDBDatabase {
+ public:
+ static scoped_ptr<LevelDBDatabase> Open(
+ const base::FilePath& file_name,
+ const LevelDBComparator* /*comparator*/) {
+ return scoped_ptr<LevelDBDatabase>(new BustedLevelDBDatabase);
+ }
+ virtual bool Get(const LevelDBSlice& key,
+ std::vector<char>& value,
+ bool& found,
+ const LevelDBSnapshot* = 0) OVERRIDE {
+ // false means IO error.
+ return false;
+ }
+};
+
+class MockLevelDBFactory : public LevelDBFactory {
+ public:
+ MockLevelDBFactory() : destroy_called_(false) {}
+ virtual scoped_ptr<LevelDBDatabase> OpenLevelDB(
+ const base::FilePath& file_name,
+ const LevelDBComparator* comparator) OVERRIDE {
+ return BustedLevelDBDatabase::Open(file_name, comparator);
+ }
+ virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE {
+ EXPECT_FALSE(destroy_called_);
+ destroy_called_ = true;
+ return false;
+ }
+ virtual ~MockLevelDBFactory() { EXPECT_TRUE(destroy_called_); }
+
+ private:
+ bool destroy_called_;
+};
+
+TEST(IndexedDBIOErrorTest, CleanUpTest) {
+ WebSecurityOrigin origin(
+ WebSecurityOrigin::createFromString("http://localhost:81"));
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+ const base::FilePath path = temp_directory.path();
+ string16 dummy_file_identifier;
+ MockLevelDBFactory mock_leveldb_factory;
+ scoped_refptr<IndexedDBBackingStore> backing_store =
+ IndexedDBBackingStore::Open(origin.databaseIdentifier(),
+ UTF8ToUTF16(path.AsUTF8Unsafe()),
+ dummy_file_identifier,
+ &mock_leveldb_factory);
+}
+
+} // namespace
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 038ef94..0b61910 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -14,6 +14,7 @@
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "content/browser/indexed_db/indexed_db_quota_client.h"
+#include "content/browser/indexed_db/webidbfactory_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/indexed_db_info.h"
#include "content/public/common/content_switches.h"
@@ -40,15 +41,14 @@ const base::FilePath::CharType IndexedDBContextImpl::kIndexedDBExtension[] =
namespace {
-void GetAllOriginsAndPaths(
- const base::FilePath& indexeddb_path,
- std::vector<GURL>* origins,
- std::vector<base::FilePath>* file_paths) {
+void GetAllOriginsAndPaths(const base::FilePath& indexeddb_path,
+ std::vector<GURL>* origins,
+ std::vector<base::FilePath>* file_paths) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
if (indexeddb_path.empty())
return;
- file_util::FileEnumerator file_enumerator(indexeddb_path,
- false, file_util::FileEnumerator::DIRECTORIES);
+ file_util::FileEnumerator file_enumerator(
+ indexeddb_path, false, file_util::FileEnumerator::DIRECTORIES);
for (base::FilePath file_path = file_enumerator.Next(); !file_path.empty();
file_path = file_enumerator.Next()) {
if (file_path.Extension() == IndexedDBContextImpl::kIndexedDBExtension) {
@@ -74,7 +74,8 @@ void ClearSessionOnlyOrigins(
std::vector<base::FilePath>::const_iterator file_path_iter =
file_paths.begin();
for (std::vector<GURL>::const_iterator iter = origins.begin();
- iter != origins.end(); ++iter, ++file_path_iter) {
+ iter != origins.end();
+ ++iter, ++file_path_iter) {
if (!special_storage_policy->IsStorageSessionOnly(*iter))
continue;
if (special_storage_policy->IsStorageProtected(*iter))
@@ -108,7 +109,10 @@ WebIDBFactory* IndexedDBContextImpl::GetIDBFactory() {
// Prime our cache of origins with existing databases so we can
// detect when dbs are newly created.
GetOriginSet();
- idb_factory_.reset(WebIDBFactory::create());
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewIndexedDB))
+ idb_factory_.reset(new content::WebIDBFactoryImpl());
+ else
+ idb_factory_.reset(WebIDBFactory::create());
}
return idb_factory_.get();
}
@@ -118,7 +122,8 @@ std::vector<GURL> IndexedDBContextImpl::GetAllOrigins() {
std::vector<GURL> origins;
std::set<GURL>* origins_set = GetOriginSet();
for (std::set<GURL>::const_iterator iter = origins_set->begin();
- iter != origins_set->end(); ++iter) {
+ iter != origins_set->end();
+ ++iter) {
origins.push_back(*iter);
}
return origins;
@@ -128,7 +133,8 @@ std::vector<IndexedDBInfo> IndexedDBContextImpl::GetAllOriginsInfo() {
std::vector<GURL> origins = GetAllOrigins();
std::vector<IndexedDBInfo> result;
for (std::vector<GURL>::const_iterator iter = origins.begin();
- iter != origins.end(); ++iter) {
+ iter != origins.end();
+ ++iter) {
const GURL& origin_url = *iter;
base::FilePath idb_directory = GetFilePath(origin_url);
@@ -202,7 +208,7 @@ base::FilePath IndexedDBContextImpl::GetFilePath(const GURL& origin_url) {
}
base::FilePath IndexedDBContextImpl::GetFilePathForTesting(
- const string16& origin_id) const {
+ const string16& origin_id) const {
return GetIndexedDBFilePath(origin_id);
}
@@ -211,7 +217,8 @@ void IndexedDBContextImpl::ConnectionOpened(const GURL& origin_url,
DCHECK_EQ(connections_[origin_url].count(connection), 0UL);
if (quota_manager_proxy()) {
quota_manager_proxy()->NotifyStorageAccessed(
- quota::QuotaClient::kIndexedDatabase, origin_url,
+ quota::QuotaClient::kIndexedDatabase,
+ origin_url,
quota::kStorageTypeTemporary);
}
connections_[origin_url].insert(connection);
@@ -232,7 +239,8 @@ void IndexedDBContextImpl::ConnectionClosed(const GURL& origin_url,
return;
if (quota_manager_proxy()) {
quota_manager_proxy()->NotifyStorageAccessed(
- quota::QuotaClient::kIndexedDatabase, origin_url,
+ quota::QuotaClient::kIndexedDatabase,
+ origin_url,
quota::kStorageTypeTemporary);
}
connections_[origin_url].erase(connection);
@@ -271,8 +279,8 @@ quota::QuotaManagerProxy* IndexedDBContextImpl::quota_manager_proxy() {
IndexedDBContextImpl::~IndexedDBContextImpl() {
WebKit::WebIDBFactory* factory = idb_factory_.release();
if (factory) {
- if (!BrowserThread::DeleteSoon(BrowserThread::WEBKIT_DEPRECATED,
- FROM_HERE, factory))
+ if (!BrowserThread::DeleteSoon(
+ BrowserThread::WEBKIT_DEPRECATED, FROM_HERE, factory))
delete factory;
}
@@ -293,18 +301,17 @@ IndexedDBContextImpl::~IndexedDBContextImpl() {
// No WEBKIT thread here means we are running in a unit test where no clean
// up is needed.
BrowserThread::PostTask(
- BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
- base::Bind(&ClearSessionOnlyOrigins,
- data_path_,
- special_storage_policy_));
+ BrowserThread::WEBKIT_DEPRECATED,
+ FROM_HERE,
+ base::Bind(
+ &ClearSessionOnlyOrigins, data_path_, special_storage_policy_));
}
base::FilePath IndexedDBContextImpl::GetIndexedDBFilePath(
const string16& origin_id) const {
DCHECK(!data_path_.empty());
- base::FilePath::StringType id =
- webkit_base::WebStringToFilePathString(origin_id).append(
- FILE_PATH_LITERAL(".indexeddb"));
+ base::FilePath::StringType id = webkit_base::WebStringToFilePathString(
+ origin_id).append(FILE_PATH_LITERAL(".indexeddb"));
return data_path_.Append(id.append(kIndexedDBExtension));
}
@@ -331,18 +338,20 @@ void IndexedDBContextImpl::QueryDiskAndUpdateQuotaUsage(
if (difference) {
origin_size_map_[origin_url] = current_disk_usage;
// quota_manager_proxy() is NULL in unit tests.
- if (quota_manager_proxy())
+ if (quota_manager_proxy()) {
quota_manager_proxy()->NotifyStorageModified(
quota::QuotaClient::kIndexedDatabase,
origin_url,
quota::kStorageTypeTemporary,
difference);
+ }
}
}
void IndexedDBContextImpl::GotUsageAndQuota(const GURL& origin_url,
quota::QuotaStatusCode status,
- int64 usage, int64 quota) {
+ int64 usage,
+ int64 quota) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(status == quota::kQuotaStatusOk || status == quota::kQuotaErrorAbort)
<< "status was " << status;
@@ -350,13 +359,17 @@ void IndexedDBContextImpl::GotUsageAndQuota(const GURL& origin_url,
// We seem to no longer care to wait around for the answer.
return;
}
- BrowserThread::PostTask(
- BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
- base::Bind(&IndexedDBContextImpl::GotUpdatedQuota, this, origin_url,
- usage, quota));
+ BrowserThread::PostTask(BrowserThread::WEBKIT_DEPRECATED,
+ FROM_HERE,
+ base::Bind(&IndexedDBContextImpl::GotUpdatedQuota,
+ this,
+ origin_url,
+ usage,
+ quota));
}
-void IndexedDBContextImpl::GotUpdatedQuota(const GURL& origin_url, int64 usage,
+void IndexedDBContextImpl::GotUpdatedQuota(const GURL& origin_url,
+ int64 usage,
int64 quota) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
space_available_map_[origin_url] = quota - usage;
@@ -365,11 +378,13 @@ void IndexedDBContextImpl::GotUpdatedQuota(const GURL& origin_url, int64 usage,
void IndexedDBContextImpl::QueryAvailableQuota(const GURL& origin_url) {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
- if (quota_manager_proxy())
+ if (quota_manager_proxy()) {
BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&IndexedDBContextImpl::QueryAvailableQuota, this,
- origin_url));
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &IndexedDBContextImpl::QueryAvailableQuota, this, origin_url));
+ }
return;
}
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -387,7 +402,8 @@ std::set<GURL>* IndexedDBContextImpl::GetOriginSet() {
std::vector<GURL> origins;
GetAllOriginsAndPaths(data_path_, &origins, NULL);
for (std::vector<GURL>::const_iterator iter = origins.begin();
- iter != origins.end(); ++iter) {
+ iter != origins.end();
+ ++iter) {
origin_set_->insert(*iter);
}
}
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h
index 48c9e07..846b2b1 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -66,8 +66,8 @@ class CONTENT_EXPORT IndexedDBContextImpl
OVERRIDE;
// Methods called by IndexedDBDispatcherHost for quota support.
- void ConnectionOpened(const GURL& origin_url, WebKit::WebIDBDatabase*);
- void ConnectionClosed(const GURL& origin_url, WebKit::WebIDBDatabase*);
+ void ConnectionOpened(const GURL& origin_url, WebKit::WebIDBDatabase* db);
+ void ConnectionClosed(const GURL& origin_url, WebKit::WebIDBDatabase* db);
void TransactionComplete(const GURL& origin_url);
bool WouldBeOverQuota(const GURL& origin_url, int64 additional_bytes);
bool IsOverQuota(const GURL& origin_url);
diff --git a/content/browser/indexed_db/indexed_db_cursor.h b/content/browser/indexed_db/indexed_db_cursor.h
new file mode 100644
index 0000000..536b359
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_cursor.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CURSOR_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CURSOR_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+
+namespace content {
+
+class IndexedDBCallbacksWrapper;
+
+class IndexedDBCursor : public base::RefCounted<IndexedDBCursor> {
+ public:
+ virtual void Advance(unsigned long count,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ virtual void ContinueFunction(
+ scoped_ptr<IndexedDBKey> key,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ virtual void DeleteFunction(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ virtual void PrefetchContinue(
+ int number_to_fetch,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ virtual void PrefetchReset(int used_prefetches, int unused_prefetches) = 0;
+ virtual void PostSuccessHandlerCallback() = 0;
+
+ protected:
+ virtual ~IndexedDBCursor() {}
+ friend class base::RefCounted<IndexedDBCursor>;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CURSOR_H_
diff --git a/content/browser/indexed_db/indexed_db_cursor_impl.cc b/content/browser/indexed_db/indexed_db_cursor_impl.cc
new file mode 100644
index 0000000..a11e880
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_cursor_impl.cc
@@ -0,0 +1,223 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_cursor_impl.h"
+
+#include "base/logging.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
+#include "content/browser/indexed_db/indexed_db_database_impl.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+
+namespace content {
+
+class IndexedDBCursorImpl::CursorIterationOperation
+ : public IndexedDBTransaction::Operation {
+ public:
+ CursorIterationOperation(scoped_refptr<IndexedDBCursorImpl> cursor,
+ scoped_ptr<IndexedDBKey> key,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : cursor_(cursor), key_(key.Pass()), callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBCursorImpl> cursor_;
+ scoped_ptr<IndexedDBKey> key_;
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class IndexedDBCursorImpl::CursorAdvanceOperation
+ : public IndexedDBTransaction::Operation {
+ public:
+ CursorAdvanceOperation(scoped_refptr<IndexedDBCursorImpl> cursor,
+ unsigned long count,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : cursor_(cursor), count_(count), callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBCursorImpl> cursor_;
+ unsigned long count_;
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class IndexedDBCursorImpl::CursorPrefetchIterationOperation
+ : public IndexedDBTransaction::Operation {
+ public:
+ CursorPrefetchIterationOperation(
+ scoped_refptr<IndexedDBCursorImpl> cursor,
+ int number_to_fetch,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : cursor_(cursor),
+ number_to_fetch_(number_to_fetch),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBCursorImpl> cursor_;
+ int number_to_fetch_;
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+IndexedDBCursorImpl::IndexedDBCursorImpl(
+ scoped_ptr<IndexedDBBackingStore::Cursor> cursor,
+ indexed_db::CursorType cursor_type,
+ IndexedDBDatabase::TaskType task_type,
+ IndexedDBTransaction* transaction,
+ int64 object_store_id)
+ : task_type_(task_type),
+ cursor_type_(cursor_type),
+ transaction_(transaction),
+ object_store_id_(object_store_id),
+ cursor_(cursor.Pass()),
+ closed_(false) {
+ transaction_->RegisterOpenCursor(this);
+}
+
+IndexedDBCursorImpl::~IndexedDBCursorImpl() {
+ transaction_->UnregisterOpenCursor(this);
+}
+
+void IndexedDBCursorImpl::ContinueFunction(
+ scoped_ptr<IndexedDBKey> key,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBCursorImpl::continue");
+
+ transaction_->ScheduleTask(
+ task_type_, new CursorIterationOperation(this, key.Pass(), callbacks));
+}
+
+void IndexedDBCursorImpl::Advance(
+ unsigned long count,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBCursorImpl::advance");
+
+ transaction_->ScheduleTask(
+ new CursorAdvanceOperation(this, count, callbacks));
+}
+
+void IndexedDBCursorImpl::CursorAdvanceOperation::Perform(
+ IndexedDBTransaction* /*transaction*/) {
+ IDB_TRACE("CursorAdvanceOperation");
+ if (!cursor_->cursor_ || !cursor_->cursor_->Advance(count_)) {
+ cursor_->cursor_.reset();
+ callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL));
+ return;
+ }
+
+ callbacks_->OnSuccess(
+ cursor_->key(), cursor_->primary_key(), cursor_->Value());
+}
+
+void IndexedDBCursorImpl::CursorIterationOperation::Perform(
+ IndexedDBTransaction* /*transaction*/) {
+ IDB_TRACE("CursorIterationOperation");
+ if (!cursor_->cursor_ || !cursor_->cursor_->ContinueFunction(key_.get())) {
+ cursor_->cursor_.reset();
+ callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL));
+ return;
+ }
+
+ callbacks_->OnSuccess(
+ cursor_->key(), cursor_->primary_key(), cursor_->Value());
+}
+
+void IndexedDBCursorImpl::DeleteFunction(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBCursorImpl::delete");
+ DCHECK_NE(transaction_->mode(), indexed_db::TRANSACTION_READ_ONLY);
+ scoped_ptr<IndexedDBKeyRange> key_range =
+ make_scoped_ptr(new IndexedDBKeyRange(cursor_->primary_key()));
+ transaction_->database()->DeleteRange(
+ transaction_->id(), object_store_id_, key_range.Pass(), callbacks);
+}
+
+void IndexedDBCursorImpl::PrefetchContinue(
+ int number_to_fetch,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBCursorImpl::prefetch_continue");
+
+ transaction_->ScheduleTask(
+ task_type_,
+ new CursorPrefetchIterationOperation(this, number_to_fetch, callbacks));
+}
+
+void IndexedDBCursorImpl::CursorPrefetchIterationOperation::Perform(
+ IndexedDBTransaction* /*transaction*/) {
+ IDB_TRACE("CursorPrefetchIterationOperation");
+
+ std::vector<IndexedDBKey> found_keys;
+ std::vector<IndexedDBKey> found_primary_keys;
+ std::vector<std::vector<char> > found_values;
+
+ if (cursor_->cursor_)
+ cursor_->saved_cursor_.reset(cursor_->cursor_->Clone());
+ const size_t max_size_estimate = 10 * 1024 * 1024;
+ size_t size_estimate = 0;
+
+ for (int i = 0; i < number_to_fetch_; ++i) {
+ if (!cursor_->cursor_ || !cursor_->cursor_->ContinueFunction(0)) {
+ cursor_->cursor_.reset();
+ break;
+ }
+
+ found_keys.push_back(cursor_->cursor_->key());
+ found_primary_keys.push_back(cursor_->cursor_->primary_key());
+
+ switch (cursor_->cursor_type_) {
+ case indexed_db::CURSOR_KEY_ONLY:
+ found_values.push_back(std::vector<char>());
+ break;
+ case indexed_db::CURSOR_KEY_AND_VALUE: {
+ std::vector<char> value;
+ value.swap(*cursor_->cursor_->Value());
+ size_estimate += value.size();
+ found_values.push_back(value);
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+ size_estimate += cursor_->cursor_->key().size_estimate();
+ size_estimate += cursor_->cursor_->primary_key().size_estimate();
+
+ if (size_estimate > max_size_estimate)
+ break;
+ }
+
+ if (!found_keys.size()) {
+ callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL));
+ return;
+ }
+
+ callbacks_->OnSuccessWithPrefetch(
+ found_keys, found_primary_keys, found_values);
+}
+
+void IndexedDBCursorImpl::PrefetchReset(int used_prefetches, int) {
+ IDB_TRACE("IndexedDBCursorImpl::prefetch_reset");
+ cursor_.swap(saved_cursor_);
+ saved_cursor_.reset();
+
+ if (closed_)
+ return;
+ if (cursor_) {
+ for (int i = 0; i < used_prefetches; ++i) {
+ bool ok = cursor_->ContinueFunction();
+ DCHECK(ok);
+ }
+ }
+}
+
+void IndexedDBCursorImpl::Close() {
+ IDB_TRACE("IndexedDBCursorImpl::close");
+ closed_ = true;
+ cursor_.reset();
+ saved_cursor_.reset();
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_cursor_impl.h b/content/browser/indexed_db/indexed_db_cursor_impl.h
new file mode 100644
index 0000000..8b79d27
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_cursor_impl.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CURSOR_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CURSOR_IMPL_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_cursor.h"
+#include "content/browser/indexed_db/indexed_db_database.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+
+namespace content {
+
+class IndexedDBDatabaseImpl;
+class IndexedDBTransaction;
+
+class IndexedDBCursorImpl : public IndexedDBCursor {
+ public:
+ static scoped_refptr<IndexedDBCursorImpl> Create(
+ scoped_ptr<IndexedDBBackingStore::Cursor> cursor,
+ indexed_db::CursorType cursor_type,
+ IndexedDBTransaction* transaction,
+ int64 object_store_id) {
+ return make_scoped_refptr(
+ new IndexedDBCursorImpl(cursor.Pass(),
+ cursor_type,
+ IndexedDBDatabase::NORMAL_TASK,
+ transaction,
+ object_store_id));
+ }
+ static scoped_refptr<IndexedDBCursorImpl> Create(
+ scoped_ptr<IndexedDBBackingStore::Cursor> cursor,
+ indexed_db::CursorType cursor_type,
+ IndexedDBDatabase::TaskType task_type,
+ IndexedDBTransaction* transaction,
+ int64 object_store_id) {
+ return make_scoped_refptr(new IndexedDBCursorImpl(
+ cursor.Pass(), cursor_type, task_type, transaction, object_store_id));
+ }
+
+ // IndexedDBCursor
+ virtual void Advance(unsigned long,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ OVERRIDE;
+ virtual void ContinueFunction(
+ scoped_ptr<IndexedDBKey> key,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) OVERRIDE;
+ virtual void DeleteFunction(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) OVERRIDE;
+ virtual void PrefetchContinue(
+ int number_to_fetch,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) OVERRIDE;
+ virtual void PrefetchReset(int used_prefetches, int unused_prefetches)
+ OVERRIDE;
+ virtual void PostSuccessHandlerCallback() OVERRIDE {}
+
+ const IndexedDBKey& key() const { return cursor_->key(); }
+ const IndexedDBKey& primary_key() const { return cursor_->primary_key(); }
+ std::vector<char>* Value() const {
+ return (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) ? NULL
+ : cursor_->Value();
+ }
+ void Close();
+
+ private:
+ IndexedDBCursorImpl(scoped_ptr<IndexedDBBackingStore::Cursor> cursor,
+ indexed_db::CursorType cursor_type,
+ IndexedDBDatabase::TaskType task_type,
+ IndexedDBTransaction* transaction,
+ int64 object_store_id);
+ virtual ~IndexedDBCursorImpl();
+
+ class CursorIterationOperation;
+ class CursorAdvanceOperation;
+ class CursorPrefetchIterationOperation;
+
+ IndexedDBDatabase::TaskType task_type_;
+ indexed_db::CursorType cursor_type_;
+ const scoped_refptr<IndexedDBTransaction> transaction_;
+ const int64 object_store_id_;
+
+ // Must be destroyed before transaction_.
+ scoped_ptr<IndexedDBBackingStore::Cursor> cursor_;
+ // Must be destroyed before transaction_.
+ scoped_ptr<IndexedDBBackingStore::Cursor> saved_cursor_;
+
+ bool closed_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_CURSOR_IMPL_H_
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
new file mode 100644
index 0000000..880e880
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "content/browser/indexed_db/indexed_db.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+
+namespace content {
+
+class IndexedDBCallbacksWrapper;
+class IndexedDBDatabaseCallbacksWrapper;
+struct IndexedDBDatabaseMetadata;
+
+// This is implemented by IndexedDBDatabaseImpl and optionally others (in order
+// to proxy calls across process barriers). All calls to these classes should be
+// non-blocking and trigger work on a background thread if necessary.
+class IndexedDBDatabase : public base::RefCounted<IndexedDBDatabase> {
+ public:
+ virtual void CreateObjectStore(int64 transaction_id,
+ int64 object_store_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment) = 0;
+ virtual void DeleteObjectStore(int64 transaction_id,
+ int64 object_store_id) = 0;
+ virtual void CreateTransaction(
+ int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::vector<int64>& object_store_ids,
+ uint16 mode) = 0;
+ virtual void Close(
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks) = 0;
+
+ // Transaction-specific operations.
+ virtual void Commit(int64 transaction_id) = 0;
+ virtual void Abort(int64 transaction_id) = 0;
+ virtual void Abort(int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseError> error) = 0;
+
+ virtual void CreateIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool unique,
+ bool multi_entry) = 0;
+ virtual void DeleteIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id) = 0;
+
+ enum TaskType {
+ NORMAL_TASK = 0,
+ PREEMPTIVE_TASK
+ };
+
+ enum PutMode {
+ ADD_OR_UPDATE,
+ ADD_ONLY,
+ CURSOR_UPDATE
+ };
+
+ static const int64 kMinimumIndexId = 30;
+
+ typedef std::vector<IndexedDBKey> IndexKeys;
+
+ virtual void Get(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ bool key_only,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ // This will swap() with value.
+ virtual void Put(int64 transaction_id,
+ int64 object_store_id,
+ std::vector<char>* value,
+ scoped_ptr<IndexedDBKey> key,
+ PutMode mode,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) = 0;
+ virtual void SetIndexKeys(int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKey> primary_key,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) = 0;
+ virtual void SetIndexesReady(int64 transaction_id,
+ int64 object_store_id,
+ const std::vector<int64>& index_ids) = 0;
+ virtual void OpenCursor(
+ int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_Range,
+ indexed_db::CursorDirection direction,
+ bool key_only,
+ TaskType task_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ virtual void Count(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ virtual void DeleteRange(
+ int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+ virtual void Clear(int64 transaction_id,
+ int64 object_store_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) = 0;
+
+ protected:
+ virtual ~IndexedDBDatabase() {}
+ friend class base::RefCounted<IndexedDBDatabase>;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_H_
diff --git a/content/browser/indexed_db/indexed_db_database_callbacks_wrapper.cc b/content/browser/indexed_db/indexed_db_database_callbacks_wrapper.cc
new file mode 100644
index 0000000..6e99f0c
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database_callbacks_wrapper.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h"
+
+namespace content {
+
+IndexedDBDatabaseCallbacksWrapper::IndexedDBDatabaseCallbacksWrapper(
+ WebKit::WebIDBDatabaseCallbacks* callbacks)
+ : callbacks_(callbacks) {}
+IndexedDBDatabaseCallbacksWrapper::~IndexedDBDatabaseCallbacksWrapper() {}
+
+void IndexedDBDatabaseCallbacksWrapper::OnForcedClose() {
+ if (!callbacks_)
+ return;
+ callbacks_->onForcedClose();
+ callbacks_.reset();
+}
+void IndexedDBDatabaseCallbacksWrapper::OnVersionChange(int64 old_version,
+ int64 new_version) {
+ if (!callbacks_)
+ return;
+ callbacks_->onVersionChange(old_version, new_version);
+}
+
+void IndexedDBDatabaseCallbacksWrapper::OnAbort(
+ int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseError> error) {
+ if (!callbacks_)
+ return;
+ callbacks_->onAbort(
+ transaction_id,
+ WebKit::WebIDBDatabaseError(error->code(), error->message()));
+}
+void IndexedDBDatabaseCallbacksWrapper::OnComplete(int64 transaction_id) {
+ if (!callbacks_)
+ return;
+ callbacks_->onComplete(transaction_id);
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h b/content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h
new file mode 100644
index 0000000..920f2d2
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_CALLBACKS_WRAPPER_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_CALLBACKS_WRAPPER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseCallbacks.h"
+
+namespace content {
+
+class IndexedDBDatabaseCallbacksWrapper
+ : public base::RefCounted<IndexedDBDatabaseCallbacksWrapper> {
+ public:
+ static scoped_refptr<IndexedDBDatabaseCallbacksWrapper> Create(
+ WebKit::WebIDBDatabaseCallbacks* callbacks) {
+ return make_scoped_refptr(new IndexedDBDatabaseCallbacksWrapper(callbacks));
+ }
+
+ virtual void OnForcedClose();
+ virtual void OnVersionChange(int64 old_version, int64 new_version);
+
+ virtual void OnAbort(int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseError> error);
+ virtual void OnComplete(int64 transaction_id);
+
+ private:
+ explicit IndexedDBDatabaseCallbacksWrapper(
+ WebKit::WebIDBDatabaseCallbacks* callbacks);
+ virtual ~IndexedDBDatabaseCallbacksWrapper();
+ friend class base::RefCounted<IndexedDBDatabaseCallbacksWrapper>;
+
+ scoped_ptr<WebKit::WebIDBDatabaseCallbacks> callbacks_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_CALLBACKS_WRAPPER_H_
diff --git a/content/browser/indexed_db/indexed_db_database_error.h b/content/browser/indexed_db/indexed_db_database_error.h
new file mode 100644
index 0000000..aa798c9
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database_error.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_ERROR_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_ERROR_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/string16.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseError.h"
+
+namespace content {
+
+class IndexedDBDatabaseError : public base::RefCounted<IndexedDBDatabaseError> {
+ public:
+ static scoped_refptr<IndexedDBDatabaseError> Create(uint16 code,
+ const string16& message) {
+ // TODO(jsbell): Assert that this is a valid WebIDBDatabaseException code.
+ return make_scoped_refptr(new IndexedDBDatabaseError(code, message));
+ }
+ static scoped_refptr<IndexedDBDatabaseError> Create(
+ const WebKit::WebIDBDatabaseError& other) {
+ return make_scoped_refptr(
+ new IndexedDBDatabaseError(other.code(), other.message()));
+ }
+
+ uint16 code() const { return code_; }
+ const string16& message() const { return message_; }
+
+ private:
+ IndexedDBDatabaseError(uint16 code, const string16& message)
+ : code_(code), message_(message) {}
+ ~IndexedDBDatabaseError() {}
+ friend class base::RefCounted<IndexedDBDatabaseError>;
+ const uint16 code_;
+ const string16 message_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_ERROR_H_
diff --git a/content/browser/indexed_db/indexed_db_database_impl.cc b/content/browser/indexed_db/indexed_db_database_impl.cc
new file mode 100644
index 0000000..9ecc611
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database_impl.cc
@@ -0,0 +1,1823 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_database_impl.h"
+
+#include <math.h>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_cursor_impl.h"
+#include "content/browser/indexed_db/indexed_db_factory_impl.h"
+#include "content/browser/indexed_db/indexed_db_index_writer.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
+
+using base::Int64ToString16;
+using WebKit::WebIDBKey;
+
+namespace content {
+
+class CreateObjectStoreOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateObjectStoreOperation(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ const IndexedDBObjectStoreMetadata& object_store_metadata)
+ : backing_store_(backing_store),
+ object_store_metadata_(object_store_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const IndexedDBObjectStoreMetadata object_store_metadata_;
+};
+
+class DeleteObjectStoreOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteObjectStoreOperation(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ const IndexedDBObjectStoreMetadata& object_store_metadata)
+ : backing_store_(backing_store),
+ object_store_metadata_(object_store_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const IndexedDBObjectStoreMetadata object_store_metadata_;
+};
+
+class IndexedDBDatabaseImpl::VersionChangeOperation
+ : public IndexedDBTransaction::Operation {
+ public:
+ VersionChangeOperation(
+ scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 transaction_id,
+ int64 version,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks)
+ : database_(database),
+ transaction_id_(transaction_id),
+ version_(version),
+ callbacks_(callbacks),
+ database_callbacks_(database_callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBDatabaseImpl> database_;
+ int64 transaction_id_;
+ int64 version_;
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
+};
+
+class CreateObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateObjectStoreAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 object_store_id)
+ : database_(database), object_store_id_(object_store_id) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBDatabaseImpl> database_;
+ const int64 object_store_id_;
+};
+
+class DeleteObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteObjectStoreAbortOperation(
+ scoped_refptr<IndexedDBDatabaseImpl> database,
+ const IndexedDBObjectStoreMetadata& object_store_metadata)
+ : database_(database), object_store_metadata_(object_store_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBDatabaseImpl> database_;
+ IndexedDBObjectStoreMetadata object_store_metadata_;
+};
+
+class IndexedDBDatabaseImpl::VersionChangeAbortOperation
+ : public IndexedDBTransaction::Operation {
+ public:
+ VersionChangeAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ const string16& previous_version,
+ int64 previous_int_version)
+ : database_(database),
+ previous_version_(previous_version),
+ previous_int_version_(previous_int_version) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ scoped_refptr<IndexedDBDatabaseImpl> database_;
+ string16 previous_version_;
+ int64 previous_int_version_;
+};
+
+class CreateIndexOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 object_store_id,
+ const IndexedDBIndexMetadata& index_metadata)
+ : backing_store_(backing_store),
+ object_store_id_(object_store_id),
+ index_metadata_(index_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 object_store_id_;
+ const IndexedDBIndexMetadata index_metadata_;
+};
+
+class DeleteIndexOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 object_store_id,
+ const IndexedDBIndexMetadata& index_metadata)
+ : backing_store_(backing_store),
+ object_store_id_(object_store_id),
+ index_metadata_(index_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 object_store_id_;
+ const IndexedDBIndexMetadata index_metadata_;
+};
+
+class CreateIndexAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ CreateIndexAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 object_store_id,
+ int64 index_id)
+ : database_(database),
+ object_store_id_(object_store_id),
+ index_id_(index_id) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBDatabaseImpl> database_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+};
+
+class DeleteIndexAbortOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteIndexAbortOperation(scoped_refptr<IndexedDBDatabaseImpl> database,
+ int64 object_store_id,
+ const IndexedDBIndexMetadata& index_metadata)
+ : database_(database),
+ object_store_id_(object_store_id),
+ index_metadata_(index_metadata) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBDatabaseImpl> database_;
+ const int64 object_store_id_;
+ const IndexedDBIndexMetadata index_metadata_;
+};
+
+class GetOperation : public IndexedDBTransaction::Operation {
+ public:
+ GetOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ const IndexedDBDatabaseMetadata& metadata,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ indexed_db::CursorType cursor_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(metadata.id),
+ object_store_id_(object_store_id),
+ index_id_(index_id),
+ key_path_(metadata.object_stores.find(object_store_id)
+ ->second.key_path),
+ auto_increment_(metadata.object_stores.find(object_store_id)
+ ->second.auto_increment),
+ key_range_(key_range.Pass()),
+ cursor_type_(cursor_type),
+ callbacks_(callbacks) {
+ DCHECK(metadata.object_stores.find(object_store_id) !=
+ metadata.object_stores.end());
+ DCHECK(metadata.object_stores.find(object_store_id)->second.id ==
+ object_store_id);
+ }
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+ const IndexedDBKeyPath key_path_;
+ const bool auto_increment_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const indexed_db::CursorType cursor_type_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class PutOperation : public IndexedDBTransaction::Operation {
+ public:
+ PutOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ const IndexedDBObjectStoreMetadata& object_store,
+ std::vector<char>* value,
+ scoped_ptr<IndexedDBKey> key,
+ IndexedDBDatabase::PutMode put_mode,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexedDBDatabase::IndexKeys>& index_keys)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_(object_store),
+ key_(key.Pass()),
+ put_mode_(put_mode),
+ callbacks_(callbacks),
+ index_ids_(index_ids),
+ index_keys_(index_keys) {
+ value_.swap(*value);
+ }
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const IndexedDBObjectStoreMetadata object_store_;
+ std::vector<char> value_;
+ scoped_ptr<IndexedDBKey> key_;
+ const IndexedDBDatabase::PutMode put_mode_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+ const std::vector<int64> index_ids_;
+ const std::vector<IndexedDBDatabase::IndexKeys> index_keys_;
+};
+
+class SetIndexesReadyOperation : public IndexedDBTransaction::Operation {
+ public:
+ explicit SetIndexesReadyOperation(size_t index_count)
+ : index_count_(index_count) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const size_t index_count_;
+};
+
+class OpenCursorOperation : public IndexedDBTransaction::Operation {
+ public:
+ OpenCursorOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ indexed_db::CursorDirection direction,
+ indexed_db::CursorType cursor_type,
+ IndexedDBDatabase::TaskType task_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ index_id_(index_id),
+ key_range_(key_range.Pass()),
+ direction_(direction),
+ cursor_type_(cursor_type),
+ task_type_(task_type),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const indexed_db::CursorDirection direction_;
+ const indexed_db::CursorType cursor_type_;
+ const IndexedDBDatabase::TaskType task_type_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class CountOperation : public IndexedDBTransaction::Operation {
+ public:
+ CountOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ index_id_(index_id),
+ key_range_(key_range.Pass()),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const int64 index_id_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class DeleteRangeOperation : public IndexedDBTransaction::Operation {
+ public:
+ DeleteRangeOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ key_range_(key_range.Pass()),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const scoped_ptr<IndexedDBKeyRange> key_range_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class ClearOperation : public IndexedDBTransaction::Operation {
+ public:
+ ClearOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
+ int64 database_id,
+ int64 object_store_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : backing_store_(backing_store),
+ database_id_(database_id),
+ object_store_id_(object_store_id),
+ callbacks_(callbacks) {}
+ virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
+
+ private:
+ const scoped_refptr<IndexedDBBackingStore> backing_store_;
+ const int64 database_id_;
+ const int64 object_store_id_;
+ const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+class IndexedDBDatabaseImpl::PendingOpenCall {
+ public:
+ PendingOpenCall(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 version)
+ : callbacks_(callbacks),
+ database_callbacks_(database_callbacks),
+ version_(version),
+ transaction_id_(transaction_id) {}
+ scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> DatabaseCallbacks() {
+ return database_callbacks_;
+ }
+ int64 Version() { return version_; }
+ int64 TransactionId() const { return transaction_id_; }
+
+ private:
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
+ int64 version_;
+ const int64 transaction_id_;
+};
+
+class IndexedDBDatabaseImpl::PendingDeleteCall {
+ public:
+ explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ : callbacks_(callbacks) {}
+ scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
+
+ private:
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
+};
+
+scoped_refptr<IndexedDBDatabaseImpl> IndexedDBDatabaseImpl::Create(
+ const string16& name,
+ IndexedDBBackingStore* database,
+ IndexedDBFactoryImpl* factory,
+ const string16& unique_identifier) {
+ scoped_refptr<IndexedDBDatabaseImpl> backend =
+ new IndexedDBDatabaseImpl(name, database, factory, unique_identifier);
+ if (!backend->OpenInternal())
+ return 0;
+ return backend;
+}
+
+namespace {
+const base::string16::value_type kNoStringVersion[] = {0};
+}
+
+IndexedDBDatabaseImpl::IndexedDBDatabaseImpl(
+ const string16& name,
+ IndexedDBBackingStore* backing_store,
+ IndexedDBFactoryImpl* factory,
+ const string16& unique_identifier)
+ : backing_store_(backing_store),
+ metadata_(name,
+ kInvalidId,
+ kNoStringVersion,
+ IndexedDBDatabaseMetadata::NO_INT_VERSION,
+ kInvalidId),
+ identifier_(unique_identifier),
+ factory_(factory),
+ running_version_change_transaction_(NULL),
+ closing_connection_(false) {
+ DCHECK(!metadata_.name.empty());
+}
+
+void IndexedDBDatabaseImpl::AddObjectStore(
+ const IndexedDBObjectStoreMetadata& object_store,
+ int64 new_max_object_store_id) {
+ DCHECK(metadata_.object_stores.find(object_store.id) ==
+ metadata_.object_stores.end());
+ if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
+ DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
+ metadata_.max_object_store_id = new_max_object_store_id;
+ }
+ metadata_.object_stores[object_store.id] = object_store;
+}
+
+void IndexedDBDatabaseImpl::RemoveObjectStore(int64 object_store_id) {
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ metadata_.object_stores.erase(object_store_id);
+}
+
+void IndexedDBDatabaseImpl::AddIndex(int64 object_store_id,
+ const IndexedDBIndexMetadata& index,
+ int64 new_max_index_id) {
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
+ object_store.indexes[index.id] = index;
+ if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
+ DCHECK_LT(object_store.max_index_id, new_max_index_id);
+ object_store.max_index_id = new_max_index_id;
+ }
+ metadata_.object_stores[object_store_id] = object_store;
+}
+
+void IndexedDBDatabaseImpl::RemoveIndex(int64 object_store_id, int64 index_id) {
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
+ object_store.indexes.erase(index_id);
+ metadata_.object_stores[object_store_id] = object_store;
+}
+
+bool IndexedDBDatabaseImpl::OpenInternal() {
+ bool success = false;
+ bool ok = backing_store_->GetIDBDatabaseMetaData(
+ metadata_.name, &metadata_, success);
+ DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
+ << " id_ = " << metadata_.id;
+ if (!ok)
+ return false;
+ if (success)
+ return backing_store_->GetObjectStores(metadata_.id,
+ &metadata_.object_stores);
+
+ return backing_store_->CreateIDBDatabaseMetaData(
+ metadata_.name, metadata_.version, metadata_.int_version, metadata_.id);
+}
+
+IndexedDBDatabaseImpl::~IndexedDBDatabaseImpl() {
+ DCHECK(transactions_.empty());
+ DCHECK(pending_open_calls_.empty());
+ DCHECK(pending_delete_calls_.empty());
+}
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBDatabaseImpl::BackingStore()
+ const {
+ return backing_store_;
+}
+
+void IndexedDBDatabaseImpl::CreateObjectStore(int64 transaction_id,
+ int64 object_store_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment) {
+ IDB_TRACE("IndexedDBDatabaseImpl::create_object_store");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) ==
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store_metadata(
+ name,
+ object_store_id,
+ key_path,
+ auto_increment,
+ IndexedDBDatabase::kMinimumIndexId);
+
+ transaction->ScheduleTask(
+ new CreateObjectStoreOperation(backing_store_, object_store_metadata),
+ new CreateObjectStoreAbortOperation(this, object_store_id));
+
+ AddObjectStore(object_store_metadata, object_store_id);
+}
+
+void CreateObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateObjectStoreOperation");
+ if (!backing_store_->CreateObjectStore(
+ transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_metadata_.id,
+ object_store_metadata_.name,
+ object_store_metadata_.key_path,
+ object_store_metadata_.auto_increment)) {
+ string16 error_string =
+ ASCIIToUTF16("Internal error creating object store '") +
+ object_store_metadata_.name + ASCIIToUTF16("'.");
+
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string);
+ transaction->Abort(error);
+ return;
+ }
+}
+
+void IndexedDBDatabaseImpl::DeleteObjectStore(int64 transaction_id,
+ int64 object_store_id) {
+ IDB_TRACE("IndexedDBDatabaseImpl::delete_object_store");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ const IndexedDBObjectStoreMetadata& object_store_metadata =
+ metadata_.object_stores[object_store_id];
+
+ transaction->ScheduleTask(
+ new DeleteObjectStoreOperation(backing_store_, object_store_metadata),
+ new DeleteObjectStoreAbortOperation(this, object_store_metadata));
+ RemoveObjectStore(object_store_id);
+}
+
+void IndexedDBDatabaseImpl::CreateIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool unique,
+ bool multi_entry) {
+ IDB_TRACE("IndexedDBDatabaseImpl::create_index");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ const IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index_id) == object_store.indexes.end());
+ const IndexedDBIndexMetadata index_metadata(
+ name, index_id, key_path, unique, multi_entry);
+
+ transaction->ScheduleTask(
+ new CreateIndexOperation(backing_store_, object_store_id, index_metadata),
+ new CreateIndexAbortOperation(this, object_store_id, index_id));
+
+ AddIndex(object_store_id, index_metadata, index_id);
+}
+
+void CreateIndexOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateIndexOperation");
+ if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_id_,
+ index_metadata_.id,
+ index_metadata_.name,
+ index_metadata_.key_path,
+ index_metadata_.unique,
+ index_metadata_.multi_entry)) {
+ string16 error_string = ASCIIToUTF16("Internal error creating index '") +
+ index_metadata_.name + ASCIIToUTF16("'.");
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
+ return;
+ }
+}
+
+void CreateIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateIndexAbortOperation");
+ DCHECK(!transaction);
+ database_->RemoveIndex(object_store_id_, index_id_);
+}
+
+void IndexedDBDatabaseImpl::DeleteIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id) {
+ IDB_TRACE("IndexedDBDatabaseImpl::delete_index");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ IndexedDBObjectStoreMetadata object_store =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
+ const IndexedDBIndexMetadata& index_metadata = object_store.indexes[index_id];
+
+ transaction->ScheduleTask(
+ new DeleteIndexOperation(backing_store_, object_store_id, index_metadata),
+ new DeleteIndexAbortOperation(this, object_store_id, index_metadata));
+
+ RemoveIndex(object_store_id, index_id);
+}
+
+void DeleteIndexOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteIndexOperation");
+ bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_id_,
+ index_metadata_.id);
+ if (!ok) {
+ string16 error_string = ASCIIToUTF16("Internal error deleting index '") +
+ index_metadata_.name + ASCIIToUTF16("'.");
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string);
+ transaction->Abort(error);
+ }
+}
+
+void DeleteIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteIndexAbortOperation");
+ DCHECK(!transaction);
+ database_->AddIndex(
+ object_store_id_, index_metadata_, IndexedDBIndexMetadata::kInvalidId);
+}
+
+void IndexedDBDatabaseImpl::Commit(int64 transaction_id) {
+ // The frontend suggests that we commit, but we may have previously initiated
+ // an abort, and so have disposed of the transaction. on_abort has already
+ // been dispatched to the frontend, so it will find out about that
+ // asynchronously.
+ if (transactions_.find(transaction_id) != transactions_.end())
+ transactions_[transaction_id]->Commit();
+}
+
+void IndexedDBDatabaseImpl::Abort(int64 transaction_id) {
+ // If the transaction is unknown, then it has already been aborted by the
+ // backend before this call so it is safe to ignore it.
+ if (transactions_.find(transaction_id) != transactions_.end())
+ transactions_[transaction_id]->Abort();
+}
+
+void IndexedDBDatabaseImpl::Abort(int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseError> error) {
+ // If the transaction is unknown, then it has already been aborted by the
+ // backend before this call so it is safe to ignore it.
+ if (transactions_.find(transaction_id) != transactions_.end())
+ transactions_[transaction_id]->Abort(error);
+}
+
+void IndexedDBDatabaseImpl::Get(
+ int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ bool key_only,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::get");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(new GetOperation(
+ backing_store_,
+ metadata_,
+ object_store_id,
+ index_id,
+ key_range.Pass(),
+ key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
+ callbacks));
+}
+
+void GetOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("GetOperation");
+
+ const IndexedDBKey* key;
+
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
+ if (key_range_->IsOnlyKey()) {
+ key = &key_range_->lower();
+ } else {
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
+ // ObjectStore Retrieval Operation
+ backing_store_cursor = backing_store_->OpenObjectStoreCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ } else if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
+ // Index Value Retrieval Operation
+ backing_store_cursor = backing_store_->OpenIndexKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ } else {
+ // Index Referenced Value Retrieval Operation
+ backing_store_cursor = backing_store_->OpenIndexCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ }
+
+ if (!backing_store_cursor) {
+ callbacks_->OnSuccess();
+ return;
+ }
+
+ key = &backing_store_cursor->key();
+ }
+
+ scoped_ptr<IndexedDBKey> primary_key;
+ bool ok;
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ // Object Store Retrieval Operation
+ std::vector<char> value;
+ ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key,
+ value);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error in get_record.")));
+ return;
+ }
+
+ if (value.empty()) {
+ callbacks_->OnSuccess();
+ return;
+ }
+
+ if (auto_increment_ && !key_path_.IsNull()) {
+ callbacks_->OnSuccess(&value, *key, key_path_);
+ return;
+ }
+
+ callbacks_->OnSuccess(&value);
+ return;
+ }
+
+ // From here we are dealing only with indexes.
+ ok = backing_store_->GetPrimaryKeyViaIndex(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key,
+ &primary_key);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error in get_primary_key_via_index.")));
+ return;
+ }
+ if (!primary_key) {
+ callbacks_->OnSuccess();
+ return;
+ }
+ if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
+ // Index Value Retrieval Operation
+ callbacks_->OnSuccess(*primary_key);
+ return;
+ }
+
+ // Index Referenced Value Retrieval Operation
+ std::vector<char> value;
+ ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *primary_key,
+ value);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error in get_record.")));
+ return;
+ }
+
+ if (value.empty()) {
+ callbacks_->OnSuccess();
+ return;
+ }
+ if (auto_increment_ && !key_path_.IsNull()) {
+ callbacks_->OnSuccess(&value, *primary_key, key_path_);
+ return;
+ }
+ callbacks_->OnSuccess(&value);
+}
+
+static scoped_ptr<IndexedDBKey> GenerateKey(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ scoped_refptr<IndexedDBTransaction> transaction,
+ int64 database_id,
+ int64 object_store_id) {
+ const int64 max_generator_value =
+ 9007199254740992LL; // Maximum integer storable as ECMAScript number.
+ int64 current_number;
+ bool ok = backing_store->GetKeyGeneratorCurrentNumber(
+ transaction->BackingStoreTransaction(),
+ database_id,
+ object_store_id,
+ current_number);
+ if (!ok) {
+ LOG(ERROR) << "Failed to get_key_generator_current_number";
+ return make_scoped_ptr(new IndexedDBKey());
+ }
+ if (current_number < 0 || current_number > max_generator_value)
+ return make_scoped_ptr(new IndexedDBKey());
+
+ return make_scoped_ptr(
+ new IndexedDBKey(current_number, WebIDBKey::NumberType));
+}
+
+static bool UpdateKeyGenerator(
+ scoped_refptr<IndexedDBBackingStore> backing_store,
+ scoped_refptr<IndexedDBTransaction> transaction,
+ int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey* key,
+ bool check_current) {
+ DCHECK(key && key->type() == WebIDBKey::NumberType);
+ return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
+ transaction->BackingStoreTransaction(),
+ database_id,
+ object_store_id,
+ static_cast<int64>(floor(key->number())) + 1,
+ check_current);
+}
+
+void IndexedDBDatabaseImpl::Put(
+ int64 transaction_id,
+ int64 object_store_id,
+ std::vector<char>* value,
+ scoped_ptr<IndexedDBKey> key,
+ PutMode put_mode,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) {
+ IDB_TRACE("IndexedDBDatabaseImpl::put");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
+
+ const IndexedDBObjectStoreMetadata object_store_metadata =
+ metadata_.object_stores[object_store_id];
+
+ DCHECK(key);
+ DCHECK(object_store_metadata.auto_increment || key->IsValid());
+ transaction->ScheduleTask(new PutOperation(backing_store_,
+ id(),
+ object_store_metadata,
+ value,
+ key.Pass(),
+ put_mode,
+ callbacks,
+ index_ids,
+ index_keys));
+}
+
+void PutOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("PutOperation");
+ DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
+ DCHECK_EQ(index_ids_.size(), index_keys_.size());
+ bool key_was_generated = false;
+
+ scoped_ptr<IndexedDBKey> key;
+ if (put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
+ object_store_.auto_increment && !key_->IsValid()) {
+ scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
+ backing_store_, transaction, database_id_, object_store_.id);
+ key_was_generated = true;
+ if (!auto_inc_key->IsValid()) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError,
+ ASCIIToUTF16("Maximum key generator value reached.")));
+ return;
+ }
+ key = auto_inc_key.Pass();
+ } else {
+ key = key_.Pass();
+ }
+
+ DCHECK(key->IsValid());
+
+ IndexedDBBackingStore::RecordIdentifier record_identifier;
+ if (put_mode_ == IndexedDBDatabase::ADD_ONLY) {
+ bool found = false;
+ bool ok = backing_store_->KeyExistsInObjectStore(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_.id,
+ *key.get(),
+ &record_identifier,
+ found);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error checking key existence.")));
+ return;
+ }
+ if (found) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError,
+ ASCIIToUTF16("Key already exists in the object store.")));
+ return;
+ }
+ }
+
+ ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
+ string16 error_message;
+ bool obeys_constraints = false;
+ bool backing_store_success =
+ IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
+ backing_store_.get(),
+ database_id_,
+ object_store_,
+ *key,
+ key_was_generated,
+ index_ids_,
+ index_keys_,
+ &index_writers,
+ &error_message,
+ &obeys_constraints);
+ if (!backing_store_success) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error: backing store error updating index keys.")));
+ return;
+ }
+ if (!obeys_constraints) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
+ return;
+ }
+
+ // Before this point, don't do any mutation. After this point, rollback the
+ // transaction in case of error.
+ backing_store_success =
+ backing_store_->PutRecord(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_.id,
+ *key.get(),
+ value_,
+ &record_identifier);
+ if (!backing_store_success) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error: backing store error performing put/add.")));
+ return;
+ }
+
+ for (size_t i = 0; i < index_writers.size(); ++i) {
+ IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
+ index_writer->WriteIndexKeys(record_identifier,
+ backing_store_,
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_.id);
+ }
+
+ if (object_store_.auto_increment &&
+ put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
+ key->type() == WebIDBKey::NumberType) {
+ bool ok = UpdateKeyGenerator(backing_store_,
+ transaction,
+ database_id_,
+ object_store_.id,
+ key.get(),
+ !key_was_generated);
+ if (!ok) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error updating key generator.")));
+ return;
+ }
+ }
+
+ callbacks_->OnSuccess(*key);
+}
+
+void IndexedDBDatabaseImpl::SetIndexKeys(
+ int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKey> primary_key,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) {
+ IDB_TRACE("IndexedDBDatabaseImpl::set_index_keys");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ scoped_refptr<IndexedDBBackingStore> store = BackingStore();
+ // TODO(jsbell): This method could be asynchronous, but we need to
+ // evaluate if it's worth the extra complexity.
+ IndexedDBBackingStore::RecordIdentifier record_identifier;
+ bool found = false;
+ bool ok =
+ store->KeyExistsInObjectStore(transaction->BackingStoreTransaction(),
+ metadata_.id,
+ object_store_id,
+ *primary_key,
+ &record_identifier,
+ found);
+ if (!ok) {
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error setting index keys.")));
+ return;
+ }
+ if (!found) {
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error setting index keys for object store."));
+ transaction->Abort(error);
+ return;
+ }
+
+ ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
+ string16 error_message;
+ bool obeys_constraints = false;
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ const IndexedDBObjectStoreMetadata& object_store_metadata =
+ metadata_.object_stores[object_store_id];
+ bool backing_store_success =
+ IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
+ store.get(),
+ id(),
+ object_store_metadata,
+ *primary_key,
+ false,
+ index_ids,
+ index_keys,
+ &index_writers,
+ &error_message,
+ &obeys_constraints);
+ if (!backing_store_success) {
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error: backing store error updating index keys.")));
+ return;
+ }
+ if (!obeys_constraints) {
+ transaction->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
+ return;
+ }
+
+ for (size_t i = 0; i < index_writers.size(); ++i) {
+ IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
+ index_writer->WriteIndexKeys(record_identifier,
+ store.get(),
+ transaction->BackingStoreTransaction(),
+ id(),
+ object_store_id);
+ }
+}
+
+void IndexedDBDatabaseImpl::SetIndexesReady(
+ int64 transaction_id,
+ int64,
+ const std::vector<int64>& index_ids) {
+ IDB_TRACE("IndexedDBObjectStoreImpl::set_indexes_ready");
+
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(IndexedDBDatabase::PREEMPTIVE_TASK,
+ new SetIndexesReadyOperation(index_ids.size()));
+}
+
+void SetIndexesReadyOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("SetIndexesReadyOperation");
+ for (size_t i = 0; i < index_count_; ++i)
+ transaction->DidCompletePreemptiveEvent();
+}
+
+void IndexedDBDatabaseImpl::OpenCursor(
+ int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ indexed_db::CursorDirection direction,
+ bool key_only,
+ TaskType task_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::open_cursor");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(new OpenCursorOperation(
+ backing_store_,
+ id(),
+ object_store_id,
+ index_id,
+ key_range.Pass(),
+ direction,
+ key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
+ task_type,
+ callbacks));
+}
+
+void OpenCursorOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("OpenCursorOperation");
+
+ // The frontend has begun indexing, so this pauses the transaction
+ // until the indexing is complete. This can't happen any earlier
+ // because we don't want to switch to early mode in case multiple
+ // indexes are being created in a row, with Put()'s in between.
+ if (task_type_ == IndexedDBDatabase::PREEMPTIVE_TASK)
+ transaction->AddPreemptiveEvent();
+
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
+ backing_store_cursor = backing_store_->OpenObjectStoreCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ direction_);
+ } else {
+ DCHECK_EQ(task_type_, IndexedDBDatabase::NORMAL_TASK);
+ if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
+ backing_store_cursor = backing_store_->OpenIndexKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ direction_);
+ } else {
+ backing_store_cursor = backing_store_->OpenIndexCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ direction_);
+ }
+ }
+
+ if (!backing_store_cursor) {
+ callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL));
+ return;
+ }
+
+ IndexedDBDatabase::TaskType task_type(
+ static_cast<IndexedDBDatabase::TaskType>(task_type_));
+ scoped_refptr<IndexedDBCursorImpl> cursor =
+ IndexedDBCursorImpl::Create(backing_store_cursor.Pass(),
+ cursor_type_,
+ task_type,
+ transaction,
+ object_store_id_);
+ callbacks_->OnSuccess(
+ cursor, cursor->key(), cursor->primary_key(), cursor->Value());
+}
+
+void IndexedDBDatabaseImpl::Count(
+ int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::count");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ DCHECK(metadata_.object_stores.find(object_store_id) !=
+ metadata_.object_stores.end());
+ transaction->ScheduleTask(new CountOperation(backing_store_,
+ id(),
+ object_store_id,
+ index_id,
+ key_range.Pass(),
+ callbacks));
+}
+
+void CountOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("CountOperation");
+ uint32 count = 0;
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
+
+ if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
+ backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ } else {
+ backing_store_cursor = backing_store_->OpenIndexKeyCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ index_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ }
+ if (!backing_store_cursor) {
+ callbacks_->OnSuccess(count);
+ return;
+ }
+
+ do {
+ ++count;
+ } while (backing_store_cursor->ContinueFunction(0));
+
+ callbacks_->OnSuccess(count);
+}
+
+void IndexedDBDatabaseImpl::DeleteRange(
+ int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::delete_range");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+
+ transaction->ScheduleTask(new DeleteRangeOperation(
+ backing_store_, id(), object_store_id, key_range.Pass(), callbacks));
+}
+
+void DeleteRangeOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteRangeOperation");
+ scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
+ backing_store_->OpenObjectStoreCursor(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ *key_range_,
+ indexed_db::CURSOR_NEXT);
+ if (backing_store_cursor) {
+ do {
+ if (!backing_store_->DeleteRecord(
+ transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_,
+ backing_store_cursor->record_identifier())) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error deleting data in range")));
+ return;
+ }
+ } while (backing_store_cursor->ContinueFunction(0));
+ }
+
+ callbacks_->OnSuccess();
+}
+
+void IndexedDBDatabaseImpl::Clear(
+ int64 transaction_id,
+ int64 object_store_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ IDB_TRACE("IndexedDBDatabaseImpl::clear");
+ TransactionMap::const_iterator trans_iterator =
+ transactions_.find(transaction_id);
+ if (trans_iterator == transactions_.end())
+ return;
+ IndexedDBTransaction* transaction = trans_iterator->second;
+ DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
+
+ transaction->ScheduleTask(
+ new ClearOperation(backing_store_, id(), object_store_id, callbacks));
+}
+
+void ClearOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("ObjectStoreClearOperation");
+ if (!backing_store_->ClearObjectStore(transaction->BackingStoreTransaction(),
+ database_id_,
+ object_store_id_)) {
+ callbacks_->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error clearing object store")));
+ return;
+ }
+ callbacks_->OnSuccess();
+}
+
+void DeleteObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteObjectStoreOperation");
+ bool ok =
+ backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
+ transaction->database()->id(),
+ object_store_metadata_.id);
+ if (!ok) {
+ string16 error_string =
+ ASCIIToUTF16("Internal error deleting object store '") +
+ object_store_metadata_.name + ASCIIToUTF16("'.");
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, error_string);
+ transaction->Abort(error);
+ }
+}
+
+void IndexedDBDatabaseImpl::VersionChangeOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("VersionChangeOperation");
+ int64 database_id = database_->id();
+ int64 old_version = database_->metadata_.int_version;
+ DCHECK_GT(version_, old_version);
+ database_->metadata_.int_version = version_;
+ if (!database_->backing_store_->UpdateIDBDatabaseIntVersion(
+ transaction->BackingStoreTransaction(),
+ database_id,
+ database_->metadata_.int_version)) {
+ scoped_refptr<IndexedDBDatabaseError> error =
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error writing data to stable storage when "
+ "updating version."));
+ callbacks_->OnError(error);
+ transaction->Abort(error);
+ return;
+ }
+ DCHECK(!database_->pending_second_half_open_);
+ database_->pending_second_half_open_.reset(new PendingOpenCall(
+ callbacks_, database_callbacks_, transaction_id_, version_));
+ callbacks_->OnUpgradeNeeded(old_version, database_, database_->metadata());
+}
+
+void IndexedDBDatabaseImpl::TransactionStarted(
+ IndexedDBTransaction* transaction) {
+
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ DCHECK(!running_version_change_transaction_);
+ running_version_change_transaction_ = transaction;
+ }
+}
+
+void IndexedDBDatabaseImpl::TransactionFinished(
+ IndexedDBTransaction* transaction) {
+
+ DCHECK(transactions_.find(transaction->id()) != transactions_.end());
+ DCHECK_EQ(transactions_[transaction->id()], transaction);
+ transactions_.erase(transaction->id());
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ DCHECK_EQ(transaction, running_version_change_transaction_);
+ running_version_change_transaction_ = NULL;
+ }
+}
+
+void IndexedDBDatabaseImpl::TransactionFinishedAndAbortFired(
+ IndexedDBTransaction* transaction) {
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ if (pending_second_half_open_) {
+ pending_second_half_open_->Callbacks()
+ ->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionAbortError,
+ ASCIIToUTF16("Version change transaction was aborted in "
+ "upgradeneeded event handler.")));
+ pending_second_half_open_.reset();
+ }
+ ProcessPendingCalls();
+ }
+}
+
+void IndexedDBDatabaseImpl::TransactionFinishedAndCompleteFired(
+ IndexedDBTransaction* transaction) {
+ if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
+ DCHECK(pending_second_half_open_);
+ if (pending_second_half_open_) {
+ DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
+ DCHECK(metadata_.id != kInvalidId);
+ pending_second_half_open_->Callbacks()->OnSuccess(this, this->metadata());
+ pending_second_half_open_.reset();
+ }
+ ProcessPendingCalls();
+ }
+}
+
+size_t IndexedDBDatabaseImpl::ConnectionCount() const {
+ // This does not include pending open calls, as those should not block version
+ // changes and deletes.
+ return database_callbacks_set_.size();
+}
+
+void IndexedDBDatabaseImpl::ProcessPendingCalls() {
+ if (pending_second_half_open_) {
+ DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
+ DCHECK(metadata_.id != kInvalidId);
+ scoped_ptr<PendingOpenCall> pending_call = pending_second_half_open_.Pass();
+ pending_call->Callbacks()->OnSuccess(this, this->metadata());
+ // Fall through when complete, as pending opens may be unblocked.
+ }
+
+ if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
+ DCHECK(pending_run_version_change_transaction_call_->Version() >
+ metadata_.int_version);
+ scoped_ptr<PendingOpenCall> pending_call =
+ pending_run_version_change_transaction_call_.Pass();
+ RunVersionChangeTransactionFinal(pending_call->Callbacks(),
+ pending_call->DatabaseCallbacks(),
+ pending_call->TransactionId(),
+ pending_call->Version());
+ DCHECK_EQ(static_cast<size_t>(1), ConnectionCount());
+ // Fall through would be a no-op, since transaction must complete
+ // asynchronously.
+ DCHECK(IsDeleteDatabaseBlocked());
+ DCHECK(IsOpenConnectionBlocked());
+ return;
+ }
+
+ if (!IsDeleteDatabaseBlocked()) {
+ PendingDeleteCallList pending_delete_calls;
+ pending_delete_calls_.swap(pending_delete_calls);
+ while (!pending_delete_calls.empty()) {
+ // Only the first delete call will delete the database, but each must fire
+ // callbacks.
+ scoped_ptr<PendingDeleteCall> pending_delete_call(
+ pending_delete_calls.front());
+ pending_delete_calls.pop_front();
+ DeleteDatabaseFinal(pending_delete_call->Callbacks());
+ }
+ // delete_database_final should never re-queue calls.
+ DCHECK(pending_delete_calls_.empty());
+ // Fall through when complete, as pending opens may be unblocked.
+ }
+
+ if (!IsOpenConnectionBlocked()) {
+ PendingOpenCallList pending_open_calls;
+ pending_open_calls_.swap(pending_open_calls);
+ while (!pending_open_calls.empty()) {
+ scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
+ pending_open_calls.pop_front();
+ OpenConnection(pending_open_call->Callbacks(),
+ pending_open_call->DatabaseCallbacks(),
+ pending_open_call->TransactionId(),
+ pending_open_call->Version());
+ }
+ }
+}
+
+void IndexedDBDatabaseImpl::CreateTransaction(
+ int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::vector<int64>& object_store_ids,
+ uint16 mode) {
+
+ DCHECK(database_callbacks_set_.has(callbacks));
+
+ scoped_refptr<IndexedDBTransaction> transaction =
+ IndexedDBTransaction::Create(
+ transaction_id,
+ callbacks,
+ object_store_ids,
+ static_cast<indexed_db::TransactionMode>(mode),
+ this);
+ DCHECK(transactions_.find(transaction_id) == transactions_.end());
+ transactions_[transaction_id] = transaction;
+}
+
+bool IndexedDBDatabaseImpl::IsOpenConnectionBlocked() const {
+ return !pending_delete_calls_.empty() ||
+ running_version_change_transaction_ ||
+ pending_run_version_change_transaction_call_;
+}
+
+void IndexedDBDatabaseImpl::OpenConnection(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 version) {
+ DCHECK(backing_store_.get());
+
+ // TODO(jsbell): Should have a priority queue so that higher version
+ // requests are processed first. http://crbug.com/225850
+ if (IsOpenConnectionBlocked()) {
+ pending_open_calls_.push_back(new PendingOpenCall(
+ callbacks, database_callbacks, transaction_id, version));
+ return;
+ }
+
+ if (metadata_.id == kInvalidId) {
+ // The database was deleted then immediately re-opened; OpenInternal()
+ // recreates it in the backing store.
+ if (OpenInternal()) {
+ DCHECK_EQ(metadata_.int_version,
+ IndexedDBDatabaseMetadata::NO_INT_VERSION);
+ } else {
+ string16 message;
+ scoped_refptr<IndexedDBDatabaseError> error;
+ if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
+ message = ASCIIToUTF16(
+ "Internal error opening database with no version specified.");
+ else
+ message =
+ ASCIIToUTF16("Internal error opening database with version ") +
+ Int64ToString16(version);
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError, message));
+ return;
+ }
+ }
+
+ // We infer that the database didn't exist from its lack of either type of
+ // version.
+ bool is_new_database =
+ metadata_.version == kNoStringVersion &&
+ metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
+
+ if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
+ // For unit tests only - skip upgrade steps. Calling from script with
+ // DEFAULT_INT_VERSION throws exception.
+ // TODO(jsbell): Assert that we're executing a unit test.
+ DCHECK(is_new_database);
+ database_callbacks_set_.insert(database_callbacks);
+ callbacks->OnSuccess(this, this->metadata());
+ return;
+ }
+
+ if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
+ if (!is_new_database) {
+ database_callbacks_set_.insert(database_callbacks);
+ callbacks->OnSuccess(this, this->metadata());
+ return;
+ }
+ // Spec says: If no version is specified and no database exists, set
+ // database version to 1.
+ version = 1;
+ }
+
+ if (version > metadata_.int_version) {
+ database_callbacks_set_.insert(database_callbacks);
+ RunVersionChangeTransaction(
+ callbacks, database_callbacks, transaction_id, version);
+ return;
+ }
+ if (version < metadata_.int_version) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionVersionError,
+ ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
+ ASCIIToUTF16(") is less than the existing version (") +
+ Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
+ return;
+ }
+ DCHECK_EQ(version, metadata_.int_version);
+ database_callbacks_set_.insert(database_callbacks);
+ callbacks->OnSuccess(this, this->metadata());
+}
+
+void IndexedDBDatabaseImpl::RunVersionChangeTransaction(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 requested_version) {
+
+ DCHECK(callbacks);
+ DCHECK(database_callbacks_set_.has(database_callbacks));
+ if (ConnectionCount() > 1) {
+ // Front end ensures the event is not fired at connections that have
+ // close_pending set.
+ for (DatabaseCallbacksSet::const_iterator it =
+ database_callbacks_set_.begin();
+ it != database_callbacks_set_.end();
+ ++it) {
+ if (*it != database_callbacks.get())
+ (*it)->OnVersionChange(metadata_.int_version, requested_version);
+ }
+ // TODO(jsbell): Remove the call to on_blocked and instead wait
+ // until the frontend tells us that all the "versionchange" events
+ // have been delivered. http://crbug.com/100123
+ callbacks->OnBlocked(metadata_.int_version);
+
+ DCHECK(!pending_run_version_change_transaction_call_);
+ pending_run_version_change_transaction_call_.reset(new PendingOpenCall(
+ callbacks, database_callbacks, transaction_id, requested_version));
+ return;
+ }
+ RunVersionChangeTransactionFinal(
+ callbacks, database_callbacks, transaction_id, requested_version);
+}
+
+void IndexedDBDatabaseImpl::RunVersionChangeTransactionFinal(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 requested_version) {
+
+ std::vector<int64> object_store_ids;
+ CreateTransaction(transaction_id,
+ database_callbacks,
+ object_store_ids,
+ indexed_db::TRANSACTION_VERSION_CHANGE);
+ scoped_refptr<IndexedDBTransaction> transaction =
+ transactions_[transaction_id];
+
+ transaction->ScheduleTask(
+ new VersionChangeOperation(this,
+ transaction_id,
+ requested_version,
+ callbacks,
+ database_callbacks),
+ new VersionChangeAbortOperation(
+ this, metadata_.version, metadata_.int_version));
+
+ DCHECK(!pending_second_half_open_);
+}
+
+void IndexedDBDatabaseImpl::DeleteDatabase(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+
+ if (IsDeleteDatabaseBlocked()) {
+ for (DatabaseCallbacksSet::const_iterator it =
+ database_callbacks_set_.begin();
+ it != database_callbacks_set_.end();
+ ++it) {
+ // Front end ensures the event is not fired at connections that have
+ // close_pending set.
+ (*it)->OnVersionChange(metadata_.int_version,
+ IndexedDBDatabaseMetadata::NO_INT_VERSION);
+ }
+ // TODO(jsbell): Only fire on_blocked if there are open
+ // connections after the VersionChangeEvents are received, not
+ // just set up to fire. http://crbug.com/100123
+ callbacks->OnBlocked(metadata_.int_version);
+ pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
+ return;
+ }
+ DeleteDatabaseFinal(callbacks);
+}
+
+bool IndexedDBDatabaseImpl::IsDeleteDatabaseBlocked() const {
+ return !!ConnectionCount();
+}
+
+void IndexedDBDatabaseImpl::DeleteDatabaseFinal(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
+ DCHECK(!IsDeleteDatabaseBlocked());
+ DCHECK(backing_store_);
+ if (!backing_store_->DeleteDatabase(metadata_.name)) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error deleting database.")));
+ return;
+ }
+ metadata_.version = kNoStringVersion;
+ metadata_.id = kInvalidId;
+ metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
+ metadata_.object_stores.clear();
+ callbacks->OnSuccess();
+}
+
+void IndexedDBDatabaseImpl::Close(
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks) {
+ DCHECK(callbacks);
+ DCHECK(database_callbacks_set_.has(callbacks));
+
+ // Close outstanding transactions from the closing connection. This
+ // can not happen if the close is requested by the connection itself
+ // as the front-end defers the close until all transactions are
+ // complete, so something unusual has happened e.g. unexpected
+ // process termination.
+ {
+ TransactionMap transactions(transactions_);
+ for (TransactionMap::const_iterator it = transactions.begin(),
+ end = transactions.end();
+ it != end;
+ ++it) {
+ if (it->second->connection() == callbacks)
+ it->second->Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Connection is closing.")));
+ }
+ }
+
+ database_callbacks_set_.erase(callbacks);
+ if (pending_second_half_open_ &&
+ pending_second_half_open_->DatabaseCallbacks() == callbacks) {
+ pending_second_half_open_->Callbacks()
+ ->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionAbortError,
+ ASCIIToUTF16("The connection was closed.")));
+ pending_second_half_open_.reset();
+ }
+
+ // process_pending_calls allows the inspector to process a pending open call
+ // and call close, reentering IndexedDBDatabaseImpl::close. Then the
+ // backend would be removed both by the inspector closing its connection, and
+ // by the connection that first called close.
+ // To avoid that situation, don't proceed in case of reentrancy.
+ if (closing_connection_)
+ return;
+ base::AutoReset<bool> ClosingConnection(&closing_connection_, true);
+ ProcessPendingCalls();
+
+ // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
+ if (!ConnectionCount() && !pending_open_calls_.size() &&
+ !pending_delete_calls_.size()) {
+ DCHECK(transactions_.empty());
+
+ backing_store_ = NULL;
+
+ // This check should only be false in unit tests.
+ // TODO(jsbell): Assert factory_ || we're executing a unit test.
+ if (factory_)
+ factory_->RemoveIDBDatabaseBackend(identifier_);
+ }
+}
+
+void CreateObjectStoreAbortOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("CreateObjectStoreAbortOperation");
+ DCHECK(!transaction);
+ database_->RemoveObjectStore(object_store_id_);
+}
+
+void DeleteObjectStoreAbortOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("DeleteObjectStoreAbortOperation");
+ DCHECK(!transaction);
+ database_->AddObjectStore(object_store_metadata_,
+ IndexedDBObjectStoreMetadata::kInvalidId);
+}
+
+void IndexedDBDatabaseImpl::VersionChangeAbortOperation::Perform(
+ IndexedDBTransaction* transaction) {
+ IDB_TRACE("VersionChangeAbortOperation");
+ DCHECK(!transaction);
+ database_->metadata_.version = previous_version_;
+ database_->metadata_.int_version = previous_int_version_;
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_database_impl.h b/content/browser/indexed_db/indexed_db_database_impl.h
new file mode 100644
index 0000000..d71cf0e
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database_impl.h
@@ -0,0 +1,204 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_IMPL_H_
+
+#include <list>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
+#include "content/browser/indexed_db/list_set.h"
+
+namespace content {
+
+class IndexedDBBackingStore;
+class IndexedDBDatabase;
+class IndexedDBFactoryImpl;
+class IndexedDBTransaction;
+
+class IndexedDBDatabaseImpl : public IndexedDBDatabase {
+ public:
+ static scoped_refptr<IndexedDBDatabaseImpl> Create(
+ const string16& name,
+ IndexedDBBackingStore* database,
+ IndexedDBFactoryImpl* factory,
+ const string16& unique_identifier);
+ scoped_refptr<IndexedDBBackingStore> BackingStore() const;
+
+ static const int64 kInvalidId = 0;
+ int64 id() const { return metadata_.id; }
+ void AddObjectStore(const IndexedDBObjectStoreMetadata& metadata,
+ int64 new_max_object_store_id);
+ void RemoveObjectStore(int64 object_store_id);
+ void AddIndex(int64 object_store_id,
+ const IndexedDBIndexMetadata& metadata,
+ int64 new_max_index_id);
+ void RemoveIndex(int64 object_store_id, int64 index_id);
+
+ void OpenConnection(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 version);
+ void DeleteDatabase(scoped_refptr<IndexedDBCallbacksWrapper> callbacks);
+ const IndexedDBDatabaseMetadata& metadata() const { return metadata_; }
+
+ // IndexedDBDatabase
+ virtual void CreateObjectStore(int64 transaction_id,
+ int64 object_store_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment) OVERRIDE;
+ virtual void DeleteObjectStore(int64 transaction_id, int64 object_store_id)
+ OVERRIDE;
+ virtual void CreateTransaction(
+ int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::vector<int64>& object_store_ids,
+ uint16 mode) OVERRIDE;
+ virtual void Close(scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks)
+ OVERRIDE;
+
+ virtual void Commit(int64 transaction_id) OVERRIDE;
+ virtual void Abort(int64 transaction_id) OVERRIDE;
+ virtual void Abort(int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseError> error) OVERRIDE;
+
+ virtual void CreateIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ const string16& name,
+ const IndexedDBKeyPath& key_path,
+ bool unique,
+ bool multi_entry) OVERRIDE;
+ virtual void DeleteIndex(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id) OVERRIDE;
+
+ IndexedDBTransactionCoordinator& transaction_coordinator() {
+ return transaction_coordinator_;
+ }
+
+ void TransactionStarted(IndexedDBTransaction* transaction);
+ void TransactionFinished(IndexedDBTransaction* transaction);
+ void TransactionFinishedAndCompleteFired(IndexedDBTransaction* transaction);
+ void TransactionFinishedAndAbortFired(IndexedDBTransaction* transaction);
+
+ virtual void Get(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ bool key_only,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks) OVERRIDE;
+ virtual void Put(int64 transaction_id,
+ int64 object_store_id,
+ std::vector<char>* value,
+ scoped_ptr<IndexedDBKey> key,
+ PutMode mode,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) OVERRIDE;
+ virtual void SetIndexKeys(int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKey> primary_key,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexKeys>& index_keys) OVERRIDE;
+ virtual void SetIndexesReady(int64 transaction_id,
+ int64 object_store_id,
+ const std::vector<int64>& index_ids) OVERRIDE;
+ virtual void OpenCursor(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ indexed_db::CursorDirection,
+ bool key_only,
+ TaskType task_type,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ OVERRIDE;
+ virtual void Count(int64 transaction_id,
+ int64 object_store_id,
+ int64 index_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ OVERRIDE;
+ virtual void DeleteRange(int64 transaction_id,
+ int64 object_store_id,
+ scoped_ptr<IndexedDBKeyRange> key_range,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ OVERRIDE;
+ virtual void Clear(int64 transaction_id,
+ int64 object_store_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
+ OVERRIDE;
+
+ private:
+ IndexedDBDatabaseImpl(const string16& name,
+ IndexedDBBackingStore* database,
+ IndexedDBFactoryImpl* factory,
+ const string16& unique_identifier);
+ virtual ~IndexedDBDatabaseImpl();
+
+ bool IsOpenConnectionBlocked() const;
+ bool OpenInternal();
+ void RunVersionChangeTransaction(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 requested_version);
+ void RunVersionChangeTransactionFinal(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ int64 transaction_id,
+ int64 requested_version);
+ size_t ConnectionCount() const;
+ void ProcessPendingCalls();
+
+ bool IsDeleteDatabaseBlocked() const;
+ void DeleteDatabaseFinal(scoped_refptr<IndexedDBCallbacksWrapper> callbacks);
+
+ class VersionChangeOperation;
+
+ // When a "versionchange" transaction aborts, these restore the back-end
+ // object hierarchy.
+ class VersionChangeAbortOperation;
+
+ scoped_refptr<IndexedDBBackingStore> backing_store_;
+ IndexedDBDatabaseMetadata metadata_;
+
+ string16 identifier_;
+ // This might not need to be a scoped_refptr since the factory's lifetime is
+ // that of the page group, but it's better to be conservitive than sorry.
+ scoped_refptr<IndexedDBFactoryImpl> factory_;
+
+ IndexedDBTransactionCoordinator transaction_coordinator_;
+ IndexedDBTransaction* running_version_change_transaction_;
+
+ typedef std::map<int64, IndexedDBTransaction*> TransactionMap;
+ TransactionMap transactions_;
+
+ class PendingOpenCall;
+ typedef std::list<PendingOpenCall*> PendingOpenCallList;
+ PendingOpenCallList pending_open_calls_;
+ scoped_ptr<PendingOpenCall> pending_run_version_change_transaction_call_;
+ scoped_ptr<PendingOpenCall> pending_second_half_open_;
+
+ class PendingDeleteCall;
+ typedef std::list<PendingDeleteCall*> PendingDeleteCallList;
+ PendingDeleteCallList pending_delete_calls_;
+
+ typedef list_set<scoped_refptr<IndexedDBDatabaseCallbacksWrapper> >
+ DatabaseCallbacksSet;
+ DatabaseCallbacksSet database_callbacks_set_;
+
+ bool closing_connection_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_IMPL_H_
diff --git a/content/browser/indexed_db/indexed_db_factory.h b/content/browser/indexed_db/indexed_db_factory.h
new file mode 100644
index 0000000..b7d380a
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_factory.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_FACTORY_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_FACTORY_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/string16.h"
+
+namespace content {
+
+class IndexedDBCallbacksWrapper;
+class IndexedDBDatabase;
+class IndexedDBDatabaseCallbacksWrapper;
+
+class IndexedDBFactory : public base::RefCounted<IndexedDBFactory> {
+ public:
+ static scoped_refptr<IndexedDBFactory> Create();
+
+ virtual void GetDatabaseNames(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const string16& database_identifier,
+ const string16& data_dir) = 0;
+ virtual void Open(
+ const string16& name,
+ int64 version,
+ int64 transaction_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ const string16& database_identifier,
+ const string16& data_dir) = 0;
+ virtual void DeleteDatabase(
+ const string16& name,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const string16& database_identifier,
+ const string16& data_dir) = 0;
+
+ protected:
+ virtual ~IndexedDBFactory() {}
+ friend class base::RefCounted<IndexedDBFactory>;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_FACTORY_H_
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc
new file mode 100644
index 0000000..b8b6263
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_factory_impl.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_database_impl.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
+
+namespace content {
+
+template <typename K, typename M>
+static void CleanWeakMap(std::map<K, base::WeakPtr<M> >& map) {
+ std::map<K, base::WeakPtr<M> > other;
+ other.swap(map);
+
+ typename std::map<K, base::WeakPtr<M> >::const_iterator iter = other.begin();
+ while (iter != other.end()) {
+ if (iter->second.get())
+ map[iter->first] = iter->second;
+ ++iter;
+ }
+}
+
+static string16 ComputeFileIdentifier(const string16& database_identifier) {
+ string16 suffix(ASCIIToUTF16("@1"));
+ string16 result(database_identifier);
+ result.insert(result.end(), suffix.begin(), suffix.end());
+ return result;
+}
+
+static string16 ComputeUniqueIdentifier(const string16& name,
+ const string16& database_identifier) {
+ return ComputeFileIdentifier(database_identifier) + name;
+}
+
+IndexedDBFactoryImpl::IndexedDBFactoryImpl() {}
+
+IndexedDBFactoryImpl::~IndexedDBFactoryImpl() {}
+
+void IndexedDBFactoryImpl::RemoveIDBDatabaseBackend(
+ const string16& unique_identifier) {
+ DCHECK(database_backend_map_.find(unique_identifier) !=
+ database_backend_map_.end());
+ database_backend_map_.erase(unique_identifier);
+}
+
+void IndexedDBFactoryImpl::GetDatabaseNames(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const string16& database_identifier,
+ const string16& data_directory) {
+ IDB_TRACE("IndexedDBFactoryImpl::get_database_names");
+ scoped_refptr<IndexedDBBackingStore> backing_store =
+ OpenBackingStore(database_identifier, data_directory);
+ if (!backing_store) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error opening backing store for "
+ "indexed_db.webkit_get_database_names.")));
+ return;
+ }
+
+ callbacks->OnSuccess(backing_store->GetDatabaseNames());
+}
+
+void IndexedDBFactoryImpl::DeleteDatabase(
+ const string16& name,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const string16& database_identifier,
+ const string16& data_directory) {
+ IDB_TRACE("IndexedDBFactoryImpl::delete_database");
+ const string16 unique_identifier =
+ ComputeUniqueIdentifier(name, database_identifier);
+
+ IndexedDBDatabaseMap::iterator it =
+ database_backend_map_.find(unique_identifier);
+ if (it != database_backend_map_.end()) {
+ // If there are any connections to the database, directly delete the
+ // database.
+ it->second->DeleteDatabase(callbacks);
+ return;
+ }
+
+ // TODO(jsbell): Everything from now on should be done on another thread.
+ scoped_refptr<IndexedDBBackingStore> backing_store =
+ OpenBackingStore(database_identifier, data_directory);
+ if (!backing_store) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error opening backing store "
+ "for indexed_db.delete_database.")));
+ return;
+ }
+
+ scoped_refptr<IndexedDBDatabaseImpl> database_backend =
+ IndexedDBDatabaseImpl::Create(
+ name, backing_store.get(), this, unique_identifier);
+ if (!database_backend) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error creating database backend for "
+ "indexed_db.delete_database.")));
+ return;
+ }
+
+ database_backend_map_[unique_identifier] = database_backend.get();
+ database_backend->DeleteDatabase(callbacks);
+ database_backend_map_.erase(unique_identifier);
+}
+
+scoped_refptr<IndexedDBBackingStore> IndexedDBFactoryImpl::OpenBackingStore(
+ const string16& database_identifier,
+ const string16& data_directory) {
+ const string16 file_identifier = ComputeFileIdentifier(database_identifier);
+ const bool open_in_memory = data_directory.empty();
+
+ IndexedDBBackingStoreMap::iterator it2 =
+ backing_store_map_.find(file_identifier);
+ if (it2 != backing_store_map_.end() && it2->second.get())
+ return it2->second.get();
+
+ scoped_refptr<IndexedDBBackingStore> backing_store;
+ if (open_in_memory) {
+ backing_store = IndexedDBBackingStore::OpenInMemory(file_identifier);
+ } else {
+ backing_store = IndexedDBBackingStore::Open(
+ database_identifier, data_directory, file_identifier);
+ }
+
+ if (backing_store) {
+ CleanWeakMap(backing_store_map_);
+ backing_store_map_[file_identifier] = backing_store->GetWeakPtr();
+ // If an in-memory database, bind lifetime to this factory instance.
+ if (open_in_memory)
+ session_only_backing_stores_.insert(backing_store);
+
+ // All backing stores associated with this factory should be of the same
+ // type.
+ DCHECK(session_only_backing_stores_.empty() || open_in_memory);
+
+ return backing_store;
+ }
+
+ return 0;
+}
+
+void IndexedDBFactoryImpl::Open(
+ const string16& name,
+ int64 version,
+ int64 transaction_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ const string16& database_identifier,
+ const string16& data_directory) {
+ IDB_TRACE("IndexedDBFactoryImpl::open");
+ const string16 unique_identifier =
+ ComputeUniqueIdentifier(name, database_identifier);
+
+ scoped_refptr<IndexedDBDatabaseImpl> database_backend;
+ IndexedDBDatabaseMap::iterator it =
+ database_backend_map_.find(unique_identifier);
+ if (it == database_backend_map_.end()) {
+ scoped_refptr<IndexedDBBackingStore> backing_store =
+ OpenBackingStore(database_identifier, data_directory);
+ if (!backing_store) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error opening backing store for indexedDB.open.")));
+ return;
+ }
+
+ database_backend = IndexedDBDatabaseImpl::Create(
+ name, backing_store.get(), this, unique_identifier);
+ if (!database_backend) {
+ callbacks->OnError(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16(
+ "Internal error creating database backend for indexedDB.open.")));
+ return;
+ }
+
+ database_backend_map_[unique_identifier] = database_backend.get();
+ } else {
+ database_backend = it->second;
+ }
+
+ database_backend->OpenConnection(
+ callbacks, database_callbacks, transaction_id, version);
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.h b/content/browser/indexed_db/indexed_db_factory_impl.h
new file mode 100644
index 0000000..c049585
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_factory_impl.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_FACTORY_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_FACTORY_IMPL_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class IndexedDBBackingStore;
+class IndexedDBDatabaseImpl;
+
+class CONTENT_EXPORT IndexedDBFactoryImpl
+ : NON_EXPORTED_BASE(public IndexedDBFactory) {
+ public:
+ static scoped_refptr<IndexedDBFactoryImpl> Create() {
+ return make_scoped_refptr(new IndexedDBFactoryImpl());
+ }
+
+ // Notifications from weak pointers.
+ virtual void RemoveIDBDatabaseBackend(const string16& unique_identifier);
+
+ virtual void GetDatabaseNames(
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const string16& database_identifier,
+ const string16& data_dir) OVERRIDE;
+ virtual void Open(
+ const string16& name,
+ int64 version,
+ int64 transaction_id,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
+ const string16& database_identifier,
+ const string16& data_dir) OVERRIDE;
+
+ virtual void DeleteDatabase(
+ const string16& name,
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
+ const string16& database_identifier,
+ const string16& data_dir) OVERRIDE;
+
+ protected:
+ IndexedDBFactoryImpl();
+ virtual ~IndexedDBFactoryImpl();
+ virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStore(
+ const string16& database_identifier,
+ const string16& data_dir);
+
+ private:
+ typedef std::map<string16, scoped_refptr<IndexedDBDatabaseImpl> >
+ IndexedDBDatabaseMap;
+ IndexedDBDatabaseMap database_backend_map_;
+
+ typedef std::map<string16, base::WeakPtr<IndexedDBBackingStore> >
+ IndexedDBBackingStoreMap;
+ IndexedDBBackingStoreMap backing_store_map_;
+
+ std::set<scoped_refptr<IndexedDBBackingStore> > session_only_backing_stores_;
+
+ // Only one instance of the factory should exist at any given time.
+ static IndexedDBFactoryImpl* idb_factory_backend_impl;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_FACTORY_IMPL_H_
diff --git a/content/browser/indexed_db/indexed_db_index_writer.cc b/content/browser/indexed_db/indexed_db_index_writer.cc
new file mode 100644
index 0000000..6104111
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_index_writer.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_index_writer.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+
+namespace content {
+
+IndexedDBObjectStoreImpl::IndexWriter::IndexWriter(
+ const IndexedDBIndexMetadata& index_metadata)
+ : index_metadata_(index_metadata) {}
+
+IndexedDBObjectStoreImpl::IndexWriter::IndexWriter(
+ const IndexedDBIndexMetadata& index_metadata,
+ const IndexedDBDatabase::IndexKeys& index_keys)
+ : index_metadata_(index_metadata), index_keys_(index_keys) {}
+
+IndexedDBObjectStoreImpl::IndexWriter::~IndexWriter() {}
+
+bool IndexedDBObjectStoreImpl::IndexWriter::VerifyIndexKeys(
+ IndexedDBBackingStore* backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ bool* can_add_keys,
+ const IndexedDBKey& primary_key,
+ string16* error_message) const {
+ *can_add_keys = false;
+ for (size_t i = 0; i < index_keys_.size(); ++i) {
+ bool ok = AddingKeyAllowed(backing_store,
+ transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ (index_keys_)[i],
+ primary_key,
+ can_add_keys);
+ if (!ok)
+ return false;
+ if (!*can_add_keys) {
+ if (error_message)
+ *error_message = ASCIIToUTF16("Unable to add key to index '") +
+ index_metadata_.name +
+ ASCIIToUTF16("': at least one key does not satisfy "
+ "the uniqueness requirements.");
+ return true;
+ }
+ }
+ *can_add_keys = true;
+ return true;
+}
+
+void IndexedDBObjectStoreImpl::IndexWriter::WriteIndexKeys(
+ const IndexedDBBackingStore::RecordIdentifier& record_identifier,
+ IndexedDBBackingStore* backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) const {
+ int64 index_id = index_metadata_.id;
+ for (size_t i = 0; i < index_keys_.size(); ++i) {
+ bool ok = backing_store->PutIndexDataForRecord(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_keys_[i],
+ record_identifier);
+ // This should have already been verified as a valid write during
+ // verify_index_keys.
+ DCHECK(ok);
+ }
+}
+
+bool IndexedDBObjectStoreImpl::IndexWriter::AddingKeyAllowed(
+ IndexedDBBackingStore* backing_store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& index_key,
+ const IndexedDBKey& primary_key,
+ bool* allowed) const {
+ *allowed = false;
+ if (!index_metadata_.unique) {
+ *allowed = true;
+ return true;
+ }
+
+ scoped_ptr<IndexedDBKey> found_primary_key;
+ bool found = false;
+ bool ok = backing_store->KeyExistsInIndex(transaction,
+ database_id,
+ object_store_id,
+ index_id,
+ index_key,
+ &found_primary_key,
+ found);
+ if (!ok)
+ return false;
+ if (!found ||
+ (primary_key.IsValid() && found_primary_key->IsEqual(primary_key)))
+ *allowed = true;
+ return true;
+}
+
+bool IndexedDBObjectStoreImpl::MakeIndexWriters(
+ scoped_refptr<IndexedDBTransaction> transaction,
+ IndexedDBBackingStore* backing_store,
+ int64 database_id,
+ const IndexedDBObjectStoreMetadata& object_store,
+ const IndexedDBKey& primary_key, // makes a copy
+ bool key_was_generated,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexedDBDatabase::IndexKeys>& index_keys,
+ ScopedVector<IndexWriter>* index_writers,
+ string16* error_message,
+ bool* completed) {
+ DCHECK_EQ(index_ids.size(), index_keys.size());
+ *completed = false;
+
+ std::map<int64, IndexedDBDatabase::IndexKeys> index_key_map;
+ for (size_t i = 0; i < index_ids.size(); ++i)
+ index_key_map[index_ids[i]] = index_keys[i];
+
+ for (IndexedDBObjectStoreMetadata::IndexMap::const_iterator it =
+ object_store.indexes.begin();
+ it != object_store.indexes.end();
+ ++it) {
+ const IndexedDBIndexMetadata& index = it->second;
+
+ IndexedDBDatabase::IndexKeys keys = index_key_map[it->first];
+ // If the object_store is using auto_increment, then any indexes with an
+ // identical key_path need to also use the primary (generated) key as a key.
+ if (key_was_generated && (index.key_path == object_store.key_path))
+ keys.push_back(primary_key);
+
+ scoped_ptr<IndexWriter> index_writer(new IndexWriter(index, keys));
+ bool can_add_keys = false;
+ bool backing_store_success =
+ index_writer->VerifyIndexKeys(backing_store,
+ transaction->BackingStoreTransaction(),
+ database_id,
+ object_store.id,
+ index.id,
+ &can_add_keys,
+ primary_key,
+ error_message);
+ if (!backing_store_success)
+ return false;
+ if (!can_add_keys)
+ return true;
+
+ index_writers->push_back(index_writer.release());
+ }
+
+ *completed = true;
+ return true;
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_index_writer.h b/content/browser/indexed_db/indexed_db_index_writer.h
new file mode 100644
index 0000000..372e6b5
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_index_writer.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_INDEX_WRITER_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_INDEX_WRITER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_database_impl.h"
+#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+
+namespace content {
+
+class IndexedDBDatabaseImpl;
+class IndexedDBTransaction;
+struct IndexedDBObjectStoreMetadata;
+
+// TODO(alecflett): this namespace is temporary until we move its contents out
+// to their own home.
+namespace IndexedDBObjectStoreImpl {
+
+class IndexWriter {
+ public:
+ explicit IndexWriter(const IndexedDBIndexMetadata& index_metadata);
+
+ IndexWriter(const IndexedDBIndexMetadata& index_metadata,
+ const IndexedDBDatabase::IndexKeys& index_keys);
+
+ bool VerifyIndexKeys(IndexedDBBackingStore* store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ bool* can_add_keys,
+ const IndexedDBKey& primary_key,
+ string16* error_message = 0) const WARN_UNUSED_RESULT;
+
+ void WriteIndexKeys(const IndexedDBBackingStore::RecordIdentifier& record,
+ IndexedDBBackingStore* store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id) const;
+
+ ~IndexWriter();
+
+ private:
+ bool AddingKeyAllowed(IndexedDBBackingStore* store,
+ IndexedDBBackingStore::Transaction* transaction,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& index_key,
+ const IndexedDBKey& primary_key,
+ bool* allowed) const WARN_UNUSED_RESULT;
+
+ const IndexedDBIndexMetadata index_metadata_;
+ IndexedDBDatabase::IndexKeys index_keys_;
+};
+
+bool MakeIndexWriters(
+ scoped_refptr<IndexedDBTransaction> transaction,
+ IndexedDBBackingStore* store,
+ int64 database_id,
+ const IndexedDBObjectStoreMetadata& metadata,
+ const IndexedDBKey& primary_key,
+ bool key_was_generated,
+ const std::vector<int64>& index_ids,
+ const std::vector<IndexedDBDatabase::IndexKeys>& index_keys,
+ ScopedVector<IndexWriter>* index_writers,
+ string16* error_message,
+ bool* completed) WARN_UNUSED_RESULT;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_INDEX_WRITER_H_
diff --git a/content/browser/indexed_db/indexed_db_internals_ui.cc b/content/browser/indexed_db/indexed_db_internals_ui.cc
index 7203768..0f7bc3e 100644
--- a/content/browser/indexed_db/indexed_db_internals_ui.cc
+++ b/content/browser/indexed_db/indexed_db_internals_ui.cc
@@ -301,8 +301,8 @@ void IndexedDBInternalsUI::OnDownloadStarted(
net::Error error) {
if (error != net::OK) {
- LOG(ERROR) << "Error downloading database dump: "
- << net::ErrorToString(error);
+ LOG(ERROR)
+ << "Error downloading database dump: " << net::ErrorToString(error);
return;
}
diff --git a/content/browser/indexed_db/indexed_db_internals_ui.h b/content/browser/indexed_db/indexed_db_internals_ui.h
index bc9a5da..1412ec0 100644
--- a/content/browser/indexed_db/indexed_db_internals_ui.h
+++ b/content/browser/indexed_db/indexed_db_internals_ui.h
@@ -13,7 +13,9 @@
#include "content/public/browser/web_ui_controller.h"
#include "net/base/net_errors.h"
-namespace base { class ListValue; }
+namespace base {
+class ListValue;
+}
namespace content {
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
new file mode 100644
index 0000000..590fe93
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding.cc
@@ -0,0 +1,1884 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+
+#include <iterator>
+#include <limits>
+#include <string>
+
+#include "base/logging.h"
+#include "base/string16.h"
+#include "base/sys_byteorder.h"
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "third_party/WebKit/public/platform/WebIDBKeyPath.h"
+
+// LevelDB stores key/value pairs. Keys and values are strings of bytes,
+// normally of type std::vector<char>.
+//
+// The keys in the backing store are variable-length tuples with different types
+// of fields. Each key in the backing store starts with a ternary prefix:
+// (database id, object store id, index id). For each, 0 is reserved for
+// meta-data.
+// The prefix makes sure that data for a specific database, object store, and
+// index are grouped together. The locality is important for performance: common
+// operations should only need a minimal number of seek operations. For example,
+// all the meta-data for a database is grouped together so that reading that
+// meta-data only requires one seek.
+//
+// Each key type has a class (in square brackets below) which knows how to
+// encode, decode, and compare that key type.
+//
+// Global meta-data have keys with prefix (0,0,0), followed by a type byte:
+//
+// <0, 0, 0, 0> =>
+// IndexedDB/LevelDB schema version [SchemaVersionKey]
+// <0, 0, 0, 1> => The maximum
+// database id ever allocated [MaxDatabaseIdKey]
+// <0, 0, 0, 2> =>
+// SerializedScriptValue version [DataVersionKey]
+// <0, 0, 0, 100, database id> => Existence
+// implies the database id is in the free list [DatabaseFreeListKey]
+// <0, 0, 0, 201, utf16 origin name, utf16 database name> => Database id
+// [DatabaseNameKey]
+//
+//
+// Database meta-data:
+//
+// Again, the prefix is followed by a type byte.
+//
+// <database id, 0, 0, 0> => utf16 origin name [DatabaseMetaDataKey]
+// <database id, 0, 0, 1> => utf16 database name [DatabaseMetaDataKey]
+// <database id, 0, 0, 2> => utf16 user version data [DatabaseMetaDataKey]
+// <database id, 0, 0, 3> => maximum object store id ever allocated
+// [DatabaseMetaDataKey]
+// <database id, 0, 0, 4> => user integer version (var int)
+// [DatabaseMetaDataKey]
+//
+//
+// Object store meta-data:
+//
+// The prefix is followed by a type byte, then a variable-length integer,
+// and then another type byte.
+//
+// <database id, 0, 0, 50, object store id, 0> => utf16 object store name
+// [ObjectStoreMetaDataKey]
+// <database id, 0, 0, 50, object store id, 1> => utf16 key path
+// [ObjectStoreMetaDataKey]
+// <database id, 0, 0, 50, object store id, 2> => has auto increment
+// [ObjectStoreMetaDataKey]
+// <database id, 0, 0, 50, object store id, 3> => is evictable
+// [ObjectStoreMetaDataKey]
+// <database id, 0, 0, 50, object store id, 4> => last "version" number
+// [ObjectStoreMetaDataKey]
+// <database id, 0, 0, 50, object store id, 5> => maximum index id ever
+// allocated [ObjectStoreMetaDataKey]
+// <database id, 0, 0, 50, object store id, 6> => has key path (vs. null)
+// [ObjectStoreMetaDataKey]
+// <database id, 0, 0, 50, object store id, 7> => key generator current
+// number [ObjectStoreMetaDataKey]
+//
+//
+// Index meta-data:
+//
+// The prefix is followed by a type byte, then two variable-length integers,
+// and then another type byte.
+//
+// <database id, 0, 0, 100, object store id, index id, 0> => utf16 index
+// name [IndexMetaDataKey]
+// <database id, 0, 0, 100, object store id, index id, 1> => are index keys
+// unique [IndexMetaDataKey]
+// <database id, 0, 0, 100, object store id, index id, 2> => utf16 key path
+// [IndexMetaDataKey]
+// <database id, 0, 0, 100, object store id, index id, 3> => is index
+// multi-entry [IndexMetaDataKey]
+//
+//
+// Other object store and index meta-data:
+//
+// The prefix is followed by a type byte. The object store and index id are
+// variable length integers, the utf16 strings are variable length strings.
+//
+// <database id, 0, 0, 150, object store id> => existence
+// implies the object store id is in the free list [ObjectStoreFreeListKey]
+// <database id, 0, 0, 151, object store id, index id> => existence
+// implies the index id is in the free list [IndexFreeListKey]
+// <database id, 0, 0, 200, utf16 object store name> => object
+// store id [ObjectStoreNamesKey]
+// <database id, 0, 0, 201, object store id, utf16 index name> => index id
+// [IndexNamesKey]
+//
+//
+// Object store data:
+//
+// The prefix is followed by a type byte. The user key is an encoded
+// IndexedDBKey.
+//
+// <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
+// IndexedDBKey.
+//
+// <database id, object store id, 2, user key> => "version" [ExistsEntryKey]
+//
+//
+// Index data:
+//
+// The prefix is followed by a type byte. The index key is an encoded
+// IndexedDBKey. The sequence number is a variable length integer.
+// The primary key is an encoded IndexedDBKey.
+//
+// <database id, object store id, index id, index key, sequence number,
+// primary key> => "version", primary key [IndexDataKey]
+//
+// (The sequence number is obsolete; it was used to allow two entries with
+// the same user (index) key in non-unique indexes prior to the inclusion of
+// the primary key in the data. The "version" field is used to weed out
+// stale
+// index data. Whenever new object store data is inserted, it gets a new
+// "version" number, and new index data is written with this number. When
+// the index is used for look-ups, entries are validated against the
+// "exists" entries, and records with old "version" numbers are deleted
+// when they are encountered in get_primary_key_via_index,
+// IndexCursorImpl::load_current_row, and
+// IndexKeyCursorImpl::load_current_row).
+
+using WebKit::WebIDBKey;
+using WebKit::WebIDBKeyPath;
+
+namespace content {
+
+// As most of the IndexedDBKeys and encoded values are short, we
+// initialize some Vectors with a default inline buffer size to reduce
+// the memory re-allocations when the Vectors are appended.
+static const size_t kDefaultInlineBufferSize = 32;
+
+static const unsigned char kIndexedDBKeyNullTypeByte = 0;
+static const unsigned char kIndexedDBKeyStringTypeByte = 1;
+static const unsigned char kIndexedDBKeyDateTypeByte = 2;
+static const unsigned char kIndexedDBKeyNumberTypeByte = 3;
+static const unsigned char kIndexedDBKeyArrayTypeByte = 4;
+static const unsigned char kIndexedDBKeyMinKeyTypeByte = 5;
+
+static const unsigned char kIndexedDBKeyPathTypeCodedByte1 = 0;
+static const unsigned char kIndexedDBKeyPathTypeCodedByte2 = 0;
+
+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 kDataVersionTypeByte = 2;
+static const unsigned char kMaxSimpleGlobalMetaDataTypeByte =
+ 3; // Insert before this and increment.
+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;
+
+static const unsigned char kObjectMetaDataTypeMaximum = 255;
+static const unsigned char kIndexMetaDataTypeMaximum = 255;
+
+const unsigned char kMinimumIndexId = 30;
+
+std::vector<char> EncodeByte(unsigned char c) {
+ std::vector<char> v;
+ v.reserve(kDefaultInlineBufferSize);
+ v.push_back(c);
+
+ DCHECK_LE(v.size(), kDefaultInlineBufferSize);
+ return v;
+}
+
+const char* DecodeByte(const char* p,
+ const char* limit,
+ unsigned char& found_char) {
+ DCHECK_GE(limit, p);
+ if (p >= limit)
+ return 0;
+
+ found_char = *p++;
+ return p;
+}
+
+std::vector<char> MaxIDBKey() { return EncodeByte(kIndexedDBKeyNullTypeByte); }
+
+std::vector<char> MinIDBKey() {
+ return EncodeByte(kIndexedDBKeyMinKeyTypeByte);
+}
+
+std::vector<char> EncodeBool(bool b) {
+ std::vector<char> ret;
+ ret.reserve(kDefaultInlineBufferSize);
+ ret.push_back(b ? 1 : 0);
+
+ DCHECK_LE(ret.size(), kDefaultInlineBufferSize);
+ return ret;
+}
+
+bool DecodeBool(const char* p, const char* limit) {
+ DCHECK_GT(limit, p);
+ return !!*p;
+}
+
+std::vector<char> EncodeInt(int64 nParam) {
+#ifndef NDEBUG
+ // Exercised by unit tests in debug only.
+ DCHECK_GE(nParam, 0);
+#endif
+ uint64 n = static_cast<uint64>(nParam);
+ std::vector<char> ret;
+ ret.reserve(kDefaultInlineBufferSize);
+
+ do {
+ unsigned char c = n;
+ ret.push_back(c);
+ n >>= 8;
+ } while (n);
+
+ DCHECK_LE(ret.size(), kDefaultInlineBufferSize);
+ return ret;
+}
+
+static int CompareInts(int64 a, int64 b) {
+#ifndef NDEBUG
+ // Exercised by unit tests in debug only.
+ DCHECK_GE(a, 0);
+ DCHECK_GE(b, 0);
+#endif
+ int64 diff = a - b;
+ if (diff < 0)
+ return -1;
+ if (diff > 0)
+ return 1;
+ return 0;
+}
+
+std::vector<char> EncodeVarInt(int64 nParam) {
+#ifndef NDEBUG
+ // Exercised by unit tests in debug only.
+ DCHECK_GE(nParam, 0);
+#endif
+ uint64 n = static_cast<uint64>(nParam);
+ std::vector<char> ret;
+ ret.reserve(kDefaultInlineBufferSize);
+
+ do {
+ unsigned char c = n & 0x7f;
+ n >>= 7;
+ if (n)
+ c |= 0x80;
+ ret.push_back(c);
+ } while (n);
+
+ DCHECK_LE(ret.size(), kDefaultInlineBufferSize);
+ return ret;
+}
+
+const char* DecodeVarInt(const char* p, const char* limit, int64& found_int) {
+ DCHECK_GE(limit, p);
+ found_int = 0;
+ int shift = 0;
+
+ do {
+ if (p >= limit)
+ return 0;
+
+ unsigned char c = *p;
+ found_int |= static_cast<int64>(c & 0x7f) << shift;
+ shift += 7;
+ } while (*p++ & 0x80);
+ return p;
+}
+
+std::vector<char> EncodeString(const string16& s) {
+ // Backing store is UTF-16BE, convert from host endianness.
+ size_t length = s.length();
+ std::vector<char> ret(length * sizeof(char16));
+ if (!length)
+ return ret;
+
+ const char16* src = s.c_str();
+ char16* dst = reinterpret_cast<char16*>(&*ret.begin());
+ for (unsigned i = 0; i < length; ++i)
+ *dst++ = htons(*src++);
+
+ return ret;
+}
+
+string16 DecodeString(const char* p, const char* limit) {
+ // Backing store is UTF-16BE, convert to host endianness.
+ DCHECK_GE(limit, p);
+ DCHECK(!((limit - p) % sizeof(char16)));
+
+ size_t length = (limit - p) / sizeof(char16);
+ string16 decoded;
+ decoded.reserve(length);
+ const char16* encoded = reinterpret_cast<const char16*>(p);
+ for (unsigned i = 0; i < length; ++i)
+ decoded.push_back(ntohs(*encoded++));
+ return decoded;
+}
+
+std::vector<char> EncodeStringWithLength(const string16& s) {
+ std::vector<char> result = EncodeVarInt(s.length());
+ std::vector<char> encoded_value = EncodeString(s);
+ result.insert(result.end(), encoded_value.begin(), encoded_value.end());
+ return result;
+}
+
+const char* DecodeStringWithLength(const char* p,
+ const char* limit,
+ string16& found_string) {
+ DCHECK_GE(limit, p);
+ int64 len;
+ p = DecodeVarInt(p, limit, len);
+ if (!p || len < 0 || p + len * 2 > limit)
+ return 0;
+
+ found_string = DecodeString(p, p + len * 2);
+ p += len * 2;
+ return p;
+}
+
+int CompareEncodedStringsWithLength(const char*& p,
+ const char* limit_p,
+ const char*& q,
+ const char* limit_q,
+ bool& ok) {
+ DCHECK_NE(&p, &q);
+ DCHECK_GT(limit_p, p);
+ DCHECK_GT(limit_q, q);
+ int64 len_p, len_q;
+ p = DecodeVarInt(p, limit_p, len_p);
+ q = DecodeVarInt(q, limit_q, len_q);
+ if (!p || !q || len_p < 0 || len_q < 0) {
+ ok = false;
+ return 0;
+ }
+ DCHECK(p && q);
+ DCHECK_GE(len_p, 0);
+ DCHECK_GE(len_q, 0);
+ DCHECK_LE(p + len_p * 2, limit_p);
+ DCHECK_LE(q + len_q * 2, limit_q);
+
+ const char* start_p = p;
+ const char* start_q = q;
+ p += len_p * 2;
+ q += len_q * 2;
+
+ if (p > limit_p || q > limit_q) {
+ ok = false;
+ return 0;
+ }
+
+ ok = true;
+ const size_t lmin = static_cast<size_t>(len_p < len_q ? len_p : len_q);
+ if (int x = memcmp(start_p, start_q, lmin * 2))
+ return x;
+
+ if (len_p == len_q)
+ return 0;
+
+ return (len_p > len_q) ? 1 : -1;
+}
+
+std::vector<char> EncodeDouble(double x) {
+ // TODO(jsbell): It would be nice if we could be byte order independent.
+ const char* p = reinterpret_cast<char*>(&x);
+ std::vector<char> v;
+ v.reserve(kDefaultInlineBufferSize);
+ v.insert(v.end(), p, p + sizeof(x));
+
+ DCHECK_LE(v.size(), kDefaultInlineBufferSize);
+ return v;
+}
+
+const char* DecodeDouble(const char* p, const char* limit, double* d) {
+ if (p + sizeof(*d) > limit)
+ return 0;
+
+ char* x = reinterpret_cast<char*>(d);
+ for (size_t i = 0; i < sizeof(*d); ++i)
+ *x++ = *p++;
+ return p;
+}
+
+std::vector<char> EncodeIDBKey(const IndexedDBKey& key) {
+ std::vector<char> ret;
+ ret.reserve(kDefaultInlineBufferSize);
+ EncodeIDBKey(key, ret);
+ return ret;
+}
+
+void EncodeIDBKey(const IndexedDBKey& key, std::vector<char>& into) {
+ size_t previous_size = into.size();
+ DCHECK(key.IsValid());
+ switch (key.type()) {
+ case WebIDBKey::NullType:
+ case WebIDBKey::InvalidType:
+ case WebIDBKey::MinType: {
+ NOTREACHED();
+ into.push_back(kIndexedDBKeyNullTypeByte);
+ return;
+ }
+ case WebIDBKey::ArrayType: {
+ into.push_back(kIndexedDBKeyArrayTypeByte);
+ size_t length = key.array().size();
+ std::vector<char> encoded_length = EncodeVarInt(length);
+ into.insert(into.end(), encoded_length.begin(), encoded_length.end());
+ for (size_t i = 0; i < length; ++i)
+ EncodeIDBKey(key.array()[i], into);
+ DCHECK_GT(into.size(), previous_size);
+ return;
+ }
+ case WebIDBKey::StringType: {
+ into.push_back(kIndexedDBKeyStringTypeByte);
+ std::vector<char> tmp = EncodeStringWithLength(key.string());
+ into.insert(into.end(), tmp.begin(), tmp.end());
+ DCHECK_GT(into.size(), previous_size);
+ return;
+ }
+ case WebIDBKey::DateType: {
+ into.push_back(kIndexedDBKeyDateTypeByte);
+ std::vector<char> tmp = EncodeDouble(key.date());
+ into.insert(into.end(), tmp.begin(), tmp.end());
+ DCHECK_EQ(static_cast<size_t>(9),
+ static_cast<size_t>(into.size() - previous_size));
+ return;
+ }
+ case WebIDBKey::NumberType: {
+ into.push_back(kIndexedDBKeyNumberTypeByte);
+ std::vector<char> tmp = EncodeDouble(key.number());
+ into.insert(into.end(), tmp.begin(), tmp.end());
+ DCHECK_EQ(static_cast<size_t>(9),
+ static_cast<size_t>(into.size() - previous_size));
+ return;
+ }
+ }
+
+ NOTREACHED();
+}
+
+const char* DecodeIDBKey(const char* p,
+ const char* limit,
+ scoped_ptr<IndexedDBKey>* found_key) {
+ DCHECK_GE(limit, p);
+ if (p >= limit)
+ return 0;
+
+ unsigned char type = *p++;
+
+ switch (type) {
+ case kIndexedDBKeyNullTypeByte:
+ *found_key = make_scoped_ptr(new IndexedDBKey());
+ return p;
+
+ case kIndexedDBKeyArrayTypeByte: {
+ int64 length;
+ p = DecodeVarInt(p, limit, length);
+ if (!p || length < 0)
+ return 0;
+ IndexedDBKey::KeyArray array;
+ while (length--) {
+ scoped_ptr<IndexedDBKey> key;
+ p = DecodeIDBKey(p, limit, &key);
+ if (!p)
+ return 0;
+ array.push_back(*key);
+ }
+ *found_key = make_scoped_ptr(new IndexedDBKey(array));
+ return p;
+ }
+ case kIndexedDBKeyStringTypeByte: {
+ string16 s;
+ p = DecodeStringWithLength(p, limit, s);
+ if (!p)
+ return 0;
+ *found_key = make_scoped_ptr(new IndexedDBKey(s));
+ return p;
+ }
+ case kIndexedDBKeyDateTypeByte: {
+ double d;
+ p = DecodeDouble(p, limit, &d);
+ if (!p)
+ return 0;
+ *found_key = make_scoped_ptr(new IndexedDBKey(d, WebIDBKey::DateType));
+ return p;
+ }
+ case kIndexedDBKeyNumberTypeByte: {
+ double d;
+ p = DecodeDouble(p, limit, &d);
+ if (!p)
+ return 0;
+ *found_key = make_scoped_ptr(new IndexedDBKey(d, WebIDBKey::NumberType));
+ return p;
+ }
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+const char* ExtractEncodedIDBKey(const char* start,
+ const char* limit,
+ std::vector<char>* result = 0) {
+ DCHECK_GT(limit, start);
+ const char* p = start;
+ if (p >= limit)
+ return 0;
+
+ unsigned char type = *p++;
+
+ switch (type) {
+ case kIndexedDBKeyNullTypeByte:
+ case kIndexedDBKeyMinKeyTypeByte:
+ break;
+ case kIndexedDBKeyArrayTypeByte: {
+ int64 length;
+ p = DecodeVarInt(p, limit, length);
+ if (!p || length < 0)
+ return 0;
+ while (length--) {
+ p = ExtractEncodedIDBKey(p, limit);
+ if (!p)
+ return 0;
+ }
+ break;
+ }
+ case kIndexedDBKeyStringTypeByte: {
+ int64 length;
+ p = DecodeVarInt(p, limit, length);
+ if (!p || length < 0 || p + length * 2 > limit)
+ return 0;
+ p += length * 2;
+ break;
+ }
+ case kIndexedDBKeyDateTypeByte:
+ case kIndexedDBKeyNumberTypeByte:
+ if (p + sizeof(double) > limit)
+ return 0;
+ p += sizeof(double);
+ break;
+ }
+
+ if (result) {
+ DCHECK(p);
+ DCHECK_LE(p, limit);
+ result->assign(start, p);
+ }
+
+ return p;
+}
+
+static WebIDBKey::Type KeyTypeByteToKeyType(unsigned char type) {
+ switch (type) {
+ case kIndexedDBKeyNullTypeByte:
+ return WebIDBKey::InvalidType;
+ case kIndexedDBKeyArrayTypeByte:
+ return WebIDBKey::ArrayType;
+ case kIndexedDBKeyStringTypeByte:
+ return WebIDBKey::StringType;
+ case kIndexedDBKeyDateTypeByte:
+ return WebIDBKey::DateType;
+ case kIndexedDBKeyNumberTypeByte:
+ return WebIDBKey::NumberType;
+ case kIndexedDBKeyMinKeyTypeByte:
+ return WebIDBKey::MinType;
+ }
+
+ NOTREACHED();
+ return WebIDBKey::InvalidType;
+}
+
+static int CompareTypes(WebIDBKey::Type a, WebIDBKey::Type b) { return b - a; }
+
+int CompareEncodedIDBKeys(const char*& ptr_a,
+ const char* limit_a,
+ const char*& ptr_b,
+ const char* limit_b,
+ bool& ok) {
+ ok = true;
+ DCHECK_NE(&ptr_a, &ptr_b);
+ DCHECK_LT(ptr_a, limit_a);
+ DCHECK_LT(ptr_b, limit_b);
+ unsigned char type_a = *ptr_a++;
+ unsigned char type_b = *ptr_b++;
+
+ if (int x = CompareTypes(KeyTypeByteToKeyType(type_a),
+ KeyTypeByteToKeyType(type_b)))
+ return x;
+
+ switch (type_a) {
+ case kIndexedDBKeyNullTypeByte:
+ case kIndexedDBKeyMinKeyTypeByte:
+ // Null type or max type; no payload to compare.
+ return 0;
+ case kIndexedDBKeyArrayTypeByte: {
+ int64 length_a, length_b;
+ ptr_a = DecodeVarInt(ptr_a, limit_a, length_a);
+ ptr_b = DecodeVarInt(ptr_b, limit_b, length_b);
+ if (!ptr_a || !ptr_b || length_a < 0 || length_b < 0) {
+ ok = false;
+ return 0;
+ }
+ for (int64 i = 0; i < length_a && i < length_b; ++i) {
+ int result = CompareEncodedIDBKeys(ptr_a, limit_a, ptr_b, limit_b, ok);
+ if (!ok || result)
+ return result;
+ }
+ if (length_a < length_b)
+ return -1;
+ if (length_a > length_b)
+ return 1;
+ return 0;
+ }
+ case kIndexedDBKeyStringTypeByte:
+ return CompareEncodedStringsWithLength(
+ ptr_a, limit_a, ptr_b, limit_b, ok);
+ case kIndexedDBKeyDateTypeByte:
+ case kIndexedDBKeyNumberTypeByte: {
+ double d, e;
+ ptr_a = DecodeDouble(ptr_a, limit_a, &d);
+ ptr_b = DecodeDouble(ptr_b, limit_b, &e);
+ DCHECK(ptr_a);
+ DCHECK(ptr_b);
+ if (!ptr_a || !ptr_b) {
+ ok = false;
+ return 0;
+ }
+ if (d < e)
+ return -1;
+ if (d > e)
+ return 1;
+ return 0;
+ }
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+int CompareEncodedIDBKeys(const std::vector<char>& key_a,
+ const std::vector<char>& key_b,
+ bool& ok) {
+ DCHECK_GE(key_a.size(), static_cast<size_t>(1));
+ DCHECK_GE(key_b.size(), static_cast<size_t>(1));
+
+ const char* ptr_a = &*key_a.begin();
+ const char* limit_a = &*key_a.rbegin() + 1;
+ const char* ptr_b = &*key_b.begin();
+ const char* limit_b = &*key_b.rbegin() + 1;
+
+ return CompareEncodedIDBKeys(ptr_a, limit_a, ptr_b, limit_b, ok);
+}
+
+std::vector<char> EncodeIDBKeyPath(const IndexedDBKeyPath& key_path) {
+ // May be typed, or may be a raw string. An invalid leading
+ // byte is used to identify typed coding. New records are
+ // always written as typed.
+ std::vector<char> ret;
+ ret.reserve(kDefaultInlineBufferSize);
+ ret.push_back(kIndexedDBKeyPathTypeCodedByte1);
+ ret.push_back(kIndexedDBKeyPathTypeCodedByte2);
+ ret.push_back(static_cast<char>(key_path.type()));
+ switch (key_path.type()) {
+ case WebIDBKeyPath::NullType:
+ break;
+ case WebIDBKeyPath::StringType: {
+ std::vector<char> encoded_string =
+ EncodeStringWithLength(key_path.string());
+ ret.insert(ret.end(), encoded_string.begin(), encoded_string.end());
+ break;
+ }
+ case WebIDBKeyPath::ArrayType: {
+ const std::vector<string16>& array = key_path.array();
+ size_t count = array.size();
+ std::vector<char> encoded_count = EncodeVarInt(count);
+ ret.insert(ret.end(), encoded_count.begin(), encoded_count.end());
+ for (size_t i = 0; i < count; ++i) {
+ std::vector<char> encoded_string = EncodeStringWithLength(array[i]);
+ ret.insert(ret.end(), encoded_string.begin(), encoded_string.end());
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+IndexedDBKeyPath DecodeIDBKeyPath(const char* p, const char* limit) {
+ // May be typed, or may be a raw string. An invalid leading
+ // byte sequence is used to identify typed coding. New records are
+ // always written as typed.
+ if (p == limit ||
+ (limit - p >= 2 && (*p != kIndexedDBKeyPathTypeCodedByte1 ||
+ *(p + 1) != kIndexedDBKeyPathTypeCodedByte2)))
+ return IndexedDBKeyPath(DecodeString(p, limit));
+ p += 2;
+
+ DCHECK_NE(p, limit);
+ WebIDBKeyPath::Type type = static_cast<WebIDBKeyPath::Type>(*p++);
+ switch (type) {
+ case WebIDBKeyPath::NullType:
+ DCHECK_EQ(p, limit);
+ return IndexedDBKeyPath();
+ case WebIDBKeyPath::StringType: {
+ string16 string;
+ p = DecodeStringWithLength(p, limit, string);
+ DCHECK_EQ(p, limit);
+ return IndexedDBKeyPath(string);
+ }
+ case WebIDBKeyPath::ArrayType: {
+ std::vector<string16> array;
+ int64 count;
+ p = DecodeVarInt(p, limit, count);
+ DCHECK(p);
+ DCHECK_GE(count, 0);
+ while (count--) {
+ string16 string;
+ p = DecodeStringWithLength(p, limit, string);
+ DCHECK(p);
+ array.push_back(string);
+ }
+ DCHECK_EQ(p, limit);
+ return IndexedDBKeyPath(array);
+ }
+ }
+ NOTREACHED();
+ return IndexedDBKeyPath();
+}
+
+namespace {
+
+template <typename KeyType>
+int Compare(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok) {
+ KeyType key_a;
+ KeyType key_b;
+
+ const char* ptr_a = KeyType::Decode(a.begin(), a.end(), &key_a);
+ DCHECK(ptr_a);
+ if (!ptr_a) {
+ ok = false;
+ return 0;
+ }
+ const char* ptr_b = KeyType::Decode(b.begin(), b.end(), &key_b);
+ DCHECK(ptr_b);
+ if (!ptr_b) {
+ ok = false;
+ return 0;
+ }
+
+ ok = true;
+ return key_a.Compare(key_b);
+}
+
+template <>
+int Compare<ExistsEntryKey>(const LevelDBSlice& a,
+ const LevelDBSlice& b,
+ bool,
+ bool& ok) {
+ KeyPrefix prefix_a;
+ KeyPrefix prefix_b;
+ const char* ptr_a = KeyPrefix::Decode(a.begin(), a.end(), &prefix_a);
+ const char* ptr_b = KeyPrefix::Decode(b.begin(), b.end(), &prefix_b);
+ DCHECK(ptr_a);
+ DCHECK(ptr_b);
+ DCHECK(prefix_a.database_id_);
+ DCHECK(prefix_a.object_store_id_);
+ DCHECK_EQ(prefix_a.index_id_, ExistsEntryKey::kSpecialIndexNumber);
+ DCHECK(prefix_b.database_id_);
+ DCHECK(prefix_b.object_store_id_);
+ DCHECK_EQ(prefix_b.index_id_, ExistsEntryKey::kSpecialIndexNumber);
+ DCHECK_NE(ptr_a, a.end());
+ DCHECK_NE(ptr_b, b.end());
+ // Prefixes are not compared - it is assumed this was already done.
+ DCHECK(!prefix_a.Compare(prefix_b));
+
+ return CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok);
+}
+
+template <>
+int Compare<ObjectStoreDataKey>(const LevelDBSlice& a,
+ const LevelDBSlice& b,
+ bool,
+ bool& ok) {
+ KeyPrefix prefix_a;
+ KeyPrefix prefix_b;
+ const char* ptr_a = KeyPrefix::Decode(a.begin(), a.end(), &prefix_a);
+ const char* ptr_b = KeyPrefix::Decode(b.begin(), b.end(), &prefix_b);
+ DCHECK(ptr_a);
+ DCHECK(ptr_b);
+ DCHECK(prefix_a.database_id_);
+ DCHECK(prefix_a.object_store_id_);
+ DCHECK_EQ(prefix_a.index_id_, ObjectStoreDataKey::kSpecialIndexNumber);
+ DCHECK(prefix_b.database_id_);
+ DCHECK(prefix_b.object_store_id_);
+ DCHECK_EQ(prefix_b.index_id_, ObjectStoreDataKey::kSpecialIndexNumber);
+ DCHECK_NE(ptr_a, a.end());
+ DCHECK_NE(ptr_b, b.end());
+ // Prefixes are not compared - it is assumed this was already done.
+ DCHECK(!prefix_a.Compare(prefix_b));
+
+ return CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok);
+}
+
+template <>
+int Compare<IndexDataKey>(const LevelDBSlice& a,
+ const LevelDBSlice& b,
+ bool ignore_duplicates,
+ bool& ok) {
+ KeyPrefix prefix_a;
+ KeyPrefix prefix_b;
+ const char* ptr_a = KeyPrefix::Decode(a.begin(), a.end(), &prefix_a);
+ const char* ptr_b = KeyPrefix::Decode(b.begin(), b.end(), &prefix_b);
+ DCHECK(ptr_a);
+ DCHECK(ptr_b);
+ DCHECK(prefix_a.database_id_);
+ DCHECK(prefix_a.object_store_id_);
+ DCHECK_GE(prefix_a.index_id_, kMinimumIndexId);
+ DCHECK(prefix_b.database_id_);
+ DCHECK(prefix_b.object_store_id_);
+ DCHECK_GE(prefix_b.index_id_, kMinimumIndexId);
+ DCHECK_NE(ptr_a, a.end());
+ DCHECK_NE(ptr_b, b.end());
+ // Prefixes are not compared - it is assumed this was already done.
+ DCHECK(!prefix_a.Compare(prefix_b));
+
+ // index key
+ int result = CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok);
+ if (!ok || result)
+ return result;
+ if (ignore_duplicates)
+ return 0;
+
+ // sequence number [optional]
+ int64 sequence_number_a = -1;
+ int64 sequence_number_b = -1;
+ if (ptr_a != a.end())
+ ptr_a = DecodeVarInt(ptr_a, a.end(), sequence_number_a);
+ if (ptr_b != b.end())
+ ptr_b = DecodeVarInt(ptr_b, b.end(), sequence_number_b);
+
+ // primary key [optional]
+ if (!ptr_a || !ptr_b)
+ return 0;
+ if (ptr_a == a.end() && ptr_b == b.end())
+ return 0;
+ if (ptr_a == a.end())
+ return -1;
+ if (ptr_b == b.end())
+ return 1;
+
+ result = CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok);
+ if (!ok || result)
+ return result;
+
+ return CompareInts(sequence_number_a, sequence_number_b);
+}
+
+int Compare(const LevelDBSlice& a,
+ const LevelDBSlice& b,
+ bool index_keys,
+ bool& ok) {
+ const char* ptr_a = a.begin();
+ const char* ptr_b = b.begin();
+ const char* end_a = a.end();
+ const char* end_b = b.end();
+
+ KeyPrefix prefix_a;
+ KeyPrefix prefix_b;
+
+ ptr_a = KeyPrefix::Decode(ptr_a, end_a, &prefix_a);
+ ptr_b = KeyPrefix::Decode(ptr_b, end_b, &prefix_b);
+ DCHECK(ptr_a);
+ DCHECK(ptr_b);
+ if (!ptr_a || !ptr_b) {
+ ok = false;
+ return 0;
+ }
+
+ ok = true;
+ if (int x = prefix_a.Compare(prefix_b))
+ return x;
+
+ if (prefix_a.type() == KeyPrefix::GLOBAL_METADATA) {
+ DCHECK_NE(ptr_a, end_a);
+ DCHECK_NE(ptr_b, end_b);
+
+ unsigned char type_byte_a = *ptr_a++;
+ unsigned char type_byte_b = *ptr_b++;
+
+ if (int x = type_byte_a - type_byte_b)
+ return x;
+ if (type_byte_a < kMaxSimpleGlobalMetaDataTypeByte)
+ return 0;
+
+ const bool ignore_duplicates = false;
+ if (type_byte_a == kDatabaseFreeListTypeByte)
+ return Compare<DatabaseFreeListKey>(a, b, ignore_duplicates, ok);
+ if (type_byte_a == kDatabaseNameTypeByte)
+ return Compare<DatabaseNameKey>(a, b, ignore_duplicates, ok);
+ }
+
+ if (prefix_a.type() == KeyPrefix::DATABASE_METADATA) {
+ DCHECK_NE(ptr_a, end_a);
+ DCHECK_NE(ptr_b, end_b);
+
+ unsigned char type_byte_a = *ptr_a++;
+ unsigned char type_byte_b = *ptr_b++;
+
+ if (int x = type_byte_a - type_byte_b)
+ return x;
+ if (type_byte_a < DatabaseMetaDataKey::MAX_SIMPLE_METADATA_TYPE)
+ return 0;
+
+ const bool ignore_duplicates = false;
+ if (type_byte_a == kObjectStoreMetaDataTypeByte)
+ return Compare<ObjectStoreMetaDataKey>(a, b, ignore_duplicates, ok);
+ if (type_byte_a == kIndexMetaDataTypeByte)
+ return Compare<IndexMetaDataKey>(a, b, ignore_duplicates, ok);
+ if (type_byte_a == kObjectStoreFreeListTypeByte)
+ return Compare<ObjectStoreFreeListKey>(a, b, ignore_duplicates, ok);
+ if (type_byte_a == kIndexFreeListTypeByte)
+ return Compare<IndexFreeListKey>(a, b, ignore_duplicates, ok);
+ if (type_byte_a == kObjectStoreNamesTypeByte)
+ return Compare<ObjectStoreNamesKey>(a, b, ignore_duplicates, ok);
+ if (type_byte_a == kIndexNamesKeyTypeByte)
+ return Compare<IndexNamesKey>(a, b, ignore_duplicates, ok);
+ }
+
+ if (prefix_a.type() == KeyPrefix::OBJECT_STORE_DATA) {
+ if (ptr_a == end_a && ptr_b == end_b)
+ return 0;
+ if (ptr_a == end_a)
+ return -1;
+ if (ptr_b == end_b)
+ return 1; // TODO(jsbell): This case of non-existing user keys should not
+ // have to be handled this way.
+
+ const bool ignore_duplicates = false;
+ return Compare<ObjectStoreDataKey>(a, b, ignore_duplicates, ok);
+ }
+ if (prefix_a.type() == KeyPrefix::EXISTS_ENTRY) {
+ if (ptr_a == end_a && ptr_b == end_b)
+ return 0;
+ if (ptr_a == end_a)
+ return -1;
+ if (ptr_b == end_b)
+ return 1; // TODO(jsbell): This case of non-existing user keys should not
+ // have to be handled this way.
+
+ const bool ignore_duplicates = false;
+ return Compare<ExistsEntryKey>(a, b, ignore_duplicates, ok);
+ }
+ if (prefix_a.type() == KeyPrefix::INDEX_DATA) {
+ if (ptr_a == end_a && ptr_b == end_b)
+ return 0;
+ if (ptr_a == end_a)
+ return -1;
+ if (ptr_b == end_b)
+ return 1; // TODO(jsbell): This case of non-existing user keys should not
+ // have to be handled this way.
+
+ bool ignore_duplicates = index_keys;
+ return Compare<IndexDataKey>(a, b, ignore_duplicates, ok);
+ }
+
+ NOTREACHED();
+ ok = false;
+ return 0;
+}
+
+} // namespace
+
+int Compare(const LevelDBSlice& a, const LevelDBSlice& b, bool index_keys) {
+ bool ok;
+ int result = Compare(a, b, index_keys, ok);
+ DCHECK(ok);
+ if (!ok)
+ return 0;
+ return result;
+}
+
+KeyPrefix::KeyPrefix()
+ : database_id_(INVALID_TYPE),
+ object_store_id_(INVALID_TYPE),
+ index_id_(INVALID_TYPE) {}
+
+KeyPrefix::KeyPrefix(int64 database_id)
+ : database_id_(database_id), object_store_id_(0), index_id_(0) {
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+}
+
+KeyPrefix::KeyPrefix(int64 database_id, int64 object_store_id)
+ : database_id_(database_id),
+ object_store_id_(object_store_id),
+ index_id_(0) {
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id));
+}
+
+KeyPrefix::KeyPrefix(int64 database_id, int64 object_store_id, int64 index_id)
+ : database_id_(database_id),
+ object_store_id_(object_store_id),
+ index_id_(index_id) {
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id));
+ DCHECK(KeyPrefix::IsValidIndexId(index_id));
+}
+
+KeyPrefix::KeyPrefix(enum Type type,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id)
+ : database_id_(database_id),
+ object_store_id_(object_store_id),
+ index_id_(index_id) {
+ DCHECK_EQ(type, INVALID_TYPE);
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id));
+}
+
+KeyPrefix KeyPrefix::CreateWithSpecialIndex(int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id));
+ DCHECK(index_id);
+ return KeyPrefix(INVALID_TYPE, database_id, object_store_id, index_id);
+}
+
+bool KeyPrefix::IsValidDatabaseId(int64 database_id) {
+ return (database_id > 0) && (database_id < KeyPrefix::kMaxDatabaseId);
+}
+
+bool KeyPrefix::IsValidObjectStoreId(int64 object_store_id) {
+ return (object_store_id > 0) &&
+ (object_store_id < KeyPrefix::kMaxObjectStoreId);
+}
+
+bool KeyPrefix::IsValidIndexId(int64 index_id) {
+ return (index_id >= kMinimumIndexId) && (index_id < KeyPrefix::kMaxIndexId);
+}
+
+const char* KeyPrefix::Decode(const char* start,
+ const char* limit,
+ KeyPrefix* result) {
+ if (start == limit)
+ return 0;
+
+ unsigned char first_byte = *start++;
+
+ int database_id_bytes = ((first_byte >> 5) & 0x7) + 1;
+ int object_store_id_bytes = ((first_byte >> 2) & 0x7) + 1;
+ int index_id_bytes = (first_byte & 0x3) + 1;
+
+ if (start + database_id_bytes + object_store_id_bytes + index_id_bytes >
+ limit)
+ return 0;
+
+ result->database_id_ = DecodeInt(start, start + database_id_bytes);
+ start += database_id_bytes;
+ result->object_store_id_ = DecodeInt(start, start + object_store_id_bytes);
+ start += object_store_id_bytes;
+ result->index_id_ = DecodeInt(start, start + index_id_bytes);
+ start += index_id_bytes;
+
+ return start;
+}
+
+std::vector<char> KeyPrefix::EncodeEmpty() {
+ const std::vector<char> result(4, 0);
+ DCHECK(EncodeInternal(0, 0, 0) == std::vector<char>(4, 0));
+ return result;
+}
+
+std::vector<char> KeyPrefix::Encode() const {
+ DCHECK(database_id_ != kInvalidId);
+ DCHECK(object_store_id_ != kInvalidId);
+ DCHECK(index_id_ != kInvalidId);
+ return EncodeInternal(database_id_, object_store_id_, index_id_);
+}
+
+std::vector<char> KeyPrefix::EncodeInternal(int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ std::vector<char> database_id_string =
+ EncodeIntSafely(database_id, kMaxDatabaseId);
+ std::vector<char> object_store_id_string =
+ EncodeIntSafely(object_store_id, kMaxObjectStoreId);
+ std::vector<char> index_id_string = EncodeIntSafely(index_id, kMaxIndexId);
+
+ DCHECK(database_id_string.size() <= kMaxDatabaseIdSizeBytes);
+ DCHECK(object_store_id_string.size() <= kMaxObjectStoreIdSizeBytes);
+ DCHECK(index_id_string.size() <= kMaxIndexIdSizeBytes);
+
+ unsigned char first_byte =
+ (database_id_string.size() - 1)
+ << (kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits) |
+ (object_store_id_string.size() - 1) << kMaxIndexIdSizeBits |
+ (index_id_string.size() - 1);
+ COMPILE_ASSERT(kMaxDatabaseIdSizeBits + kMaxObjectStoreIdSizeBits +
+ kMaxIndexIdSizeBits ==
+ sizeof(first_byte) * 8,
+ CANT_ENCODE_IDS);
+ std::vector<char> ret;
+ ret.reserve(kDefaultInlineBufferSize);
+ ret.push_back(first_byte);
+ ret.insert(ret.end(), database_id_string.begin(), database_id_string.end());
+ ret.insert(
+ ret.end(), object_store_id_string.begin(), object_store_id_string.end());
+ ret.insert(ret.end(), index_id_string.begin(), index_id_string.end());
+
+ DCHECK_LE(ret.size(), kDefaultInlineBufferSize);
+ return ret;
+}
+
+int KeyPrefix::Compare(const KeyPrefix& other) const {
+ DCHECK(database_id_ != kInvalidId);
+ DCHECK(object_store_id_ != kInvalidId);
+ DCHECK(index_id_ != kInvalidId);
+
+ if (database_id_ != other.database_id_)
+ return CompareInts(database_id_, other.database_id_);
+ if (object_store_id_ != other.object_store_id_)
+ return CompareInts(object_store_id_, other.object_store_id_);
+ if (index_id_ != other.index_id_)
+ return CompareInts(index_id_, other.index_id_);
+ return 0;
+}
+
+KeyPrefix::Type KeyPrefix::type() const {
+ DCHECK(database_id_ != kInvalidId);
+ DCHECK(object_store_id_ != kInvalidId);
+ DCHECK(index_id_ != kInvalidId);
+
+ if (!database_id_)
+ return GLOBAL_METADATA;
+ if (!object_store_id_)
+ return DATABASE_METADATA;
+ if (index_id_ == kObjectStoreDataIndexId)
+ return OBJECT_STORE_DATA;
+ if (index_id_ == kExistsEntryIndexId)
+ return EXISTS_ENTRY;
+ if (index_id_ >= kMinimumIndexId)
+ return INDEX_DATA;
+
+ NOTREACHED();
+ return INVALID_TYPE;
+}
+
+std::vector<char> SchemaVersionKey::Encode() {
+ std::vector<char> ret = KeyPrefix::EncodeEmpty();
+ ret.push_back(kSchemaVersionTypeByte);
+ return ret;
+}
+
+std::vector<char> MaxDatabaseIdKey::Encode() {
+ std::vector<char> ret = KeyPrefix::EncodeEmpty();
+ ret.push_back(kMaxDatabaseIdTypeByte);
+ return ret;
+}
+
+std::vector<char> DataVersionKey::Encode() {
+ std::vector<char> ret = KeyPrefix::EncodeEmpty();
+ ret.push_back(kDataVersionTypeByte);
+ return ret;
+}
+
+DatabaseFreeListKey::DatabaseFreeListKey() : database_id_(-1) {}
+
+const char* DatabaseFreeListKey::Decode(const char* start,
+ const char* limit,
+ DatabaseFreeListKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(!prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kDatabaseFreeListTypeByte);
+ if (p == limit)
+ return 0;
+ return DecodeVarInt(p, limit, result->database_id_);
+}
+
+std::vector<char> DatabaseFreeListKey::Encode(int64 database_id) {
+ std::vector<char> ret = KeyPrefix::EncodeEmpty();
+ ret.push_back(kDatabaseFreeListTypeByte);
+ std::vector<char> tmp = EncodeVarInt(database_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ return ret;
+}
+
+std::vector<char> DatabaseFreeListKey::EncodeMaxKey() {
+ return Encode(std::numeric_limits<int64>::max());
+}
+
+int64 DatabaseFreeListKey::DatabaseId() const {
+ DCHECK_GE(database_id_, 0);
+ return database_id_;
+}
+
+int DatabaseFreeListKey::Compare(const DatabaseFreeListKey& other) const {
+ DCHECK_GE(database_id_, 0);
+ return CompareInts(database_id_, other.database_id_);
+}
+
+const char* DatabaseNameKey::Decode(const char* start,
+ const char* limit,
+ DatabaseNameKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return p;
+ DCHECK(!prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kDatabaseNameTypeByte);
+ if (p == limit)
+ return 0;
+ p = DecodeStringWithLength(p, limit, result->origin_);
+ if (!p)
+ return 0;
+ return DecodeStringWithLength(p, limit, result->database_name_);
+}
+
+std::vector<char> DatabaseNameKey::Encode(const string16& origin,
+ const string16& database_name) {
+ std::vector<char> ret = KeyPrefix::EncodeEmpty();
+ ret.push_back(kDatabaseNameTypeByte);
+ std::vector<char> tmp = EncodeStringWithLength(origin);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ tmp = EncodeStringWithLength(database_name);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ return ret;
+}
+
+std::vector<char> DatabaseNameKey::EncodeMinKeyForOrigin(
+ const string16& origin) {
+ return Encode(origin, string16());
+}
+
+std::vector<char> DatabaseNameKey::EncodeStopKeyForOrigin(
+ const string16& origin) {
+ // just after origin in collation order
+ return EncodeMinKeyForOrigin(origin + base::char16('\x01'));
+}
+
+int DatabaseNameKey::Compare(const DatabaseNameKey& other) {
+ if (int x = origin_.compare(other.origin_))
+ return x;
+ return database_name_.compare(other.database_name_);
+}
+
+std::vector<char> DatabaseMetaDataKey::Encode(int64 database_id,
+ MetaDataType meta_data_type) {
+ KeyPrefix prefix(database_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.push_back(meta_data_type);
+ return ret;
+}
+
+ObjectStoreMetaDataKey::ObjectStoreMetaDataKey()
+ : object_store_id_(-1), meta_data_type_(-1) {}
+
+const char* ObjectStoreMetaDataKey::Decode(const char* start,
+ const char* limit,
+ ObjectStoreMetaDataKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kObjectStoreMetaDataTypeByte);
+ if (p == limit)
+ return 0;
+ p = DecodeVarInt(p, limit, result->object_store_id_);
+ if (!p)
+ return 0;
+ DCHECK(result->object_store_id_);
+ if (p == limit)
+ return 0;
+ return DecodeByte(p, limit, result->meta_data_type_);
+}
+
+std::vector<char> ObjectStoreMetaDataKey::Encode(int64 database_id,
+ int64 object_store_id,
+ unsigned char meta_data_type) {
+ KeyPrefix prefix(database_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.push_back(kObjectStoreMetaDataTypeByte);
+ std::vector<char> tmp = EncodeVarInt(object_store_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ ret.push_back(meta_data_type);
+ return ret;
+}
+
+std::vector<char> ObjectStoreMetaDataKey::EncodeMaxKey(int64 database_id) {
+ return Encode(database_id,
+ std::numeric_limits<int64>::max(),
+ kObjectMetaDataTypeMaximum);
+}
+
+std::vector<char> ObjectStoreMetaDataKey::EncodeMaxKey(int64 database_id,
+ int64 object_store_id) {
+ return Encode(database_id, object_store_id, kObjectMetaDataTypeMaximum);
+}
+
+int64 ObjectStoreMetaDataKey::ObjectStoreId() const {
+ DCHECK_GE(object_store_id_, 0);
+ return object_store_id_;
+}
+unsigned char ObjectStoreMetaDataKey::MetaDataType() const {
+ return meta_data_type_;
+}
+
+int ObjectStoreMetaDataKey::Compare(const ObjectStoreMetaDataKey& other) {
+ DCHECK_GE(object_store_id_, 0);
+ if (int x = CompareInts(object_store_id_, other.object_store_id_))
+ return x;
+ int64 result = meta_data_type_ - other.meta_data_type_;
+ if (result < 0)
+ return -1;
+ return (result > 0) ? 1 : result;
+}
+
+IndexMetaDataKey::IndexMetaDataKey()
+ : object_store_id_(-1), index_id_(-1), meta_data_type_(0) {}
+
+const char* IndexMetaDataKey::Decode(const char* start,
+ const char* limit,
+ IndexMetaDataKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kIndexMetaDataTypeByte);
+ if (p == limit)
+ return 0;
+ p = DecodeVarInt(p, limit, result->object_store_id_);
+ if (!p)
+ return 0;
+ p = DecodeVarInt(p, limit, result->index_id_);
+ if (!p)
+ return 0;
+ if (p == limit)
+ return 0;
+ return DecodeByte(p, limit, result->meta_data_type_);
+}
+
+std::vector<char> IndexMetaDataKey::Encode(int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ unsigned char meta_data_type) {
+ KeyPrefix prefix(database_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.push_back(kIndexMetaDataTypeByte);
+ std::vector<char> tmp = EncodeVarInt(object_store_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ tmp = EncodeVarInt(index_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ tmp = EncodeByte(meta_data_type);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ return ret;
+}
+
+std::vector<char> IndexMetaDataKey::EncodeMaxKey(int64 database_id,
+ int64 object_store_id) {
+ return Encode(database_id,
+ object_store_id,
+ std::numeric_limits<int64>::max(),
+ kIndexMetaDataTypeMaximum);
+}
+
+std::vector<char> IndexMetaDataKey::EncodeMaxKey(int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ return Encode(
+ database_id, object_store_id, index_id, kIndexMetaDataTypeMaximum);
+}
+
+int IndexMetaDataKey::Compare(const IndexMetaDataKey& other) {
+ DCHECK_GE(object_store_id_, 0);
+ DCHECK_GE(index_id_, 0);
+
+ if (int x = CompareInts(object_store_id_, other.object_store_id_))
+ return x;
+ if (int x = CompareInts(index_id_, other.index_id_))
+ return x;
+ return meta_data_type_ - other.meta_data_type_;
+}
+
+int64 IndexMetaDataKey::IndexId() const {
+ DCHECK_GE(index_id_, 0);
+ return index_id_;
+}
+
+ObjectStoreFreeListKey::ObjectStoreFreeListKey() : object_store_id_(-1) {}
+
+const char* ObjectStoreFreeListKey::Decode(const char* start,
+ const char* limit,
+ ObjectStoreFreeListKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kObjectStoreFreeListTypeByte);
+ if (p == limit)
+ return 0;
+ return DecodeVarInt(p, limit, result->object_store_id_);
+}
+
+std::vector<char> ObjectStoreFreeListKey::Encode(int64 database_id,
+ int64 object_store_id) {
+ KeyPrefix prefix(database_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.push_back(kObjectStoreFreeListTypeByte);
+ std::vector<char> tmp = EncodeVarInt(object_store_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ return ret;
+}
+
+std::vector<char> ObjectStoreFreeListKey::EncodeMaxKey(int64 database_id) {
+ return Encode(database_id, std::numeric_limits<int64>::max());
+}
+
+int64 ObjectStoreFreeListKey::ObjectStoreId() const {
+ DCHECK_GE(object_store_id_, 0);
+ return object_store_id_;
+}
+
+int ObjectStoreFreeListKey::Compare(const ObjectStoreFreeListKey& other) {
+ // TODO(jsbell): 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...
+ DCHECK_GE(object_store_id_, 0);
+ return CompareInts(object_store_id_, other.object_store_id_);
+}
+
+IndexFreeListKey::IndexFreeListKey() : object_store_id_(-1), index_id_(-1) {}
+
+const char* IndexFreeListKey::Decode(const char* start,
+ const char* limit,
+ IndexFreeListKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kIndexFreeListTypeByte);
+ if (p == limit)
+ return 0;
+ p = DecodeVarInt(p, limit, result->object_store_id_);
+ if (!p)
+ return 0;
+ return DecodeVarInt(p, limit, result->index_id_);
+}
+
+std::vector<char> IndexFreeListKey::Encode(int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ KeyPrefix prefix(database_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.push_back(kIndexFreeListTypeByte);
+ std::vector<char> tmp = EncodeVarInt(object_store_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ tmp = EncodeVarInt(index_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ return ret;
+}
+
+std::vector<char> IndexFreeListKey::EncodeMaxKey(int64 database_id,
+ int64 object_store_id) {
+ return Encode(
+ database_id, object_store_id, std::numeric_limits<int64>::max());
+}
+
+int IndexFreeListKey::Compare(const IndexFreeListKey& other) {
+ DCHECK_GE(object_store_id_, 0);
+ DCHECK_GE(index_id_, 0);
+ if (int x = CompareInts(object_store_id_, other.object_store_id_))
+ return x;
+ return CompareInts(index_id_, other.index_id_);
+}
+
+int64 IndexFreeListKey::ObjectStoreId() const {
+ DCHECK_GE(object_store_id_, 0);
+ return object_store_id_;
+}
+
+int64 IndexFreeListKey::IndexId() const {
+ DCHECK_GE(index_id_, 0);
+ return index_id_;
+}
+
+// TODO(jsbell): We never use this to look up object store ids,
+// because a mapping is kept in the IndexedDBDatabaseImpl. Can the
+// mapping become unreliable? Can we remove this?
+const char* ObjectStoreNamesKey::Decode(const char* start,
+ const char* limit,
+ ObjectStoreNamesKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kObjectStoreNamesTypeByte);
+ return DecodeStringWithLength(p, limit, result->object_store_name_);
+}
+
+std::vector<char> ObjectStoreNamesKey::Encode(
+ int64 database_id,
+ const string16& object_store_name) {
+ KeyPrefix prefix(database_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.push_back(kObjectStoreNamesTypeByte);
+ std::vector<char> tmp = EncodeStringWithLength(object_store_name);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ return ret;
+}
+
+int ObjectStoreNamesKey::Compare(const ObjectStoreNamesKey& other) {
+ return object_store_name_.compare(other.object_store_name_);
+}
+
+IndexNamesKey::IndexNamesKey() : object_store_id_(-1) {}
+
+// TODO(jsbell): We never use this to look up index ids, because a mapping
+// is kept at a higher level.
+const char* IndexNamesKey::Decode(const char* start,
+ const char* limit,
+ IndexNamesKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(!prefix.object_store_id_);
+ DCHECK(!prefix.index_id_);
+ if (p == limit)
+ return 0;
+ unsigned char type_byte = 0;
+ p = DecodeByte(p, limit, type_byte);
+ DCHECK_EQ(type_byte, kIndexNamesKeyTypeByte);
+ if (p == limit)
+ return 0;
+ p = DecodeVarInt(p, limit, result->object_store_id_);
+ if (!p)
+ return 0;
+ return DecodeStringWithLength(p, limit, result->index_name_);
+}
+
+std::vector<char> IndexNamesKey::Encode(int64 database_id,
+ int64 object_store_id,
+ const string16& index_name) {
+ KeyPrefix prefix(database_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.push_back(kIndexNamesKeyTypeByte);
+ std::vector<char> tmp = EncodeVarInt(object_store_id);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ tmp = EncodeStringWithLength(index_name);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ return ret;
+}
+
+int IndexNamesKey::Compare(const IndexNamesKey& other) {
+ DCHECK_GE(object_store_id_, 0);
+ if (int x = CompareInts(object_store_id_, other.object_store_id_))
+ return x;
+ return index_name_.compare(other.index_name_);
+}
+
+ObjectStoreDataKey::ObjectStoreDataKey() {}
+ObjectStoreDataKey::~ObjectStoreDataKey() {}
+
+const char* ObjectStoreDataKey::Decode(const char* start,
+ const char* end,
+ ObjectStoreDataKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, end, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(prefix.object_store_id_);
+ DCHECK_EQ(prefix.index_id_, kSpecialIndexNumber);
+ if (p == end)
+ return 0;
+ return ExtractEncodedIDBKey(p, end, &result->encoded_user_key_);
+}
+
+std::vector<char> ObjectStoreDataKey::Encode(
+ int64 database_id,
+ int64 object_store_id,
+ const std::vector<char> encoded_user_key) {
+ KeyPrefix prefix(KeyPrefix::CreateWithSpecialIndex(
+ database_id, object_store_id, kSpecialIndexNumber));
+ std::vector<char> ret = prefix.Encode();
+ ret.insert(ret.end(), encoded_user_key.begin(), encoded_user_key.end());
+
+ return ret;
+}
+
+std::vector<char> ObjectStoreDataKey::Encode(int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& user_key) {
+ return Encode(database_id, object_store_id, EncodeIDBKey(user_key));
+}
+
+int ObjectStoreDataKey::Compare(const ObjectStoreDataKey& other, bool& ok) {
+ return CompareEncodedIDBKeys(encoded_user_key_, other.encoded_user_key_, ok);
+}
+
+scoped_ptr<IndexedDBKey> ObjectStoreDataKey::user_key() const {
+ scoped_ptr<IndexedDBKey> key;
+ DecodeIDBKey(&encoded_user_key_[0],
+ &encoded_user_key_[0] + encoded_user_key_.size(),
+ &key);
+ return key.Pass();
+}
+
+const int64 ObjectStoreDataKey::kSpecialIndexNumber = kObjectStoreDataIndexId;
+
+ExistsEntryKey::ExistsEntryKey() {}
+ExistsEntryKey::~ExistsEntryKey() {}
+
+const char* ExistsEntryKey::Decode(const char* start,
+ const char* end,
+ ExistsEntryKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, end, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(prefix.object_store_id_);
+ DCHECK_EQ(prefix.index_id_, kSpecialIndexNumber);
+ if (p == end)
+ return 0;
+ return ExtractEncodedIDBKey(p, end, &result->encoded_user_key_);
+}
+
+std::vector<char> ExistsEntryKey::Encode(int64 database_id,
+ int64 object_store_id,
+ const std::vector<char>& encoded_key) {
+ KeyPrefix prefix(KeyPrefix::CreateWithSpecialIndex(
+ database_id, object_store_id, kSpecialIndexNumber));
+ std::vector<char> ret = prefix.Encode();
+ ret.insert(ret.end(), encoded_key.begin(), encoded_key.end());
+ return ret;
+}
+
+std::vector<char> ExistsEntryKey::Encode(int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& user_key) {
+ return Encode(database_id, object_store_id, EncodeIDBKey(user_key));
+}
+
+int ExistsEntryKey::Compare(const ExistsEntryKey& other, bool& ok) {
+ return CompareEncodedIDBKeys(encoded_user_key_, other.encoded_user_key_, ok);
+}
+
+scoped_ptr<IndexedDBKey> ExistsEntryKey::user_key() const {
+ scoped_ptr<IndexedDBKey> key;
+ DecodeIDBKey(&encoded_user_key_[0],
+ &encoded_user_key_[0] + encoded_user_key_.size(),
+ &key);
+ return key.Pass();
+}
+
+const int64 ExistsEntryKey::kSpecialIndexNumber = kExistsEntryIndexId;
+
+IndexDataKey::IndexDataKey()
+ : database_id_(-1),
+ object_store_id_(-1),
+ index_id_(-1),
+ sequence_number_(-1) {}
+
+IndexDataKey::~IndexDataKey() {}
+
+const char* IndexDataKey::Decode(const char* start,
+ const char* limit,
+ IndexDataKey* result) {
+ KeyPrefix prefix;
+ const char* p = KeyPrefix::Decode(start, limit, &prefix);
+ if (!p)
+ return 0;
+ DCHECK(prefix.database_id_);
+ DCHECK(prefix.object_store_id_);
+ DCHECK_GE(prefix.index_id_, kMinimumIndexId);
+ result->database_id_ = prefix.database_id_;
+ result->object_store_id_ = prefix.object_store_id_;
+ result->index_id_ = prefix.index_id_;
+ result->sequence_number_ = -1;
+ result->encoded_primary_key_ = MinIDBKey();
+
+ p = ExtractEncodedIDBKey(p, limit, &result->encoded_user_key_);
+ if (!p)
+ return 0;
+
+ // [optional] sequence number
+ if (p == limit)
+ return p;
+ p = DecodeVarInt(p, limit, result->sequence_number_);
+ if (!p)
+ return 0;
+
+ // [optional] primary key
+ if (p == limit)
+ return p;
+ p = ExtractEncodedIDBKey(p, limit, &result->encoded_primary_key_);
+ if (!p)
+ return 0;
+
+ return p;
+}
+
+std::vector<char> IndexDataKey::Encode(
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const std::vector<char>& encoded_user_key,
+ const std::vector<char>& encoded_primary_key,
+ int64 sequence_number) {
+ KeyPrefix prefix(database_id, object_store_id, index_id);
+ std::vector<char> ret = prefix.Encode();
+ ret.insert(ret.end(), encoded_user_key.begin(), encoded_user_key.end());
+ std::vector<char> tmp = EncodeVarInt(sequence_number);
+ ret.insert(ret.end(), tmp.begin(), tmp.end());
+ ret.insert(ret.end(), encoded_primary_key.begin(), encoded_primary_key.end());
+ return ret;
+}
+
+std::vector<char> IndexDataKey::Encode(int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& user_key) {
+ return Encode(database_id,
+ object_store_id,
+ index_id,
+ EncodeIDBKey(user_key),
+ MinIDBKey());
+}
+
+std::vector<char> IndexDataKey::EncodeMinKey(int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ return Encode(
+ database_id, object_store_id, index_id, MinIDBKey(), MinIDBKey());
+}
+
+std::vector<char> IndexDataKey::EncodeMaxKey(int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ return Encode(database_id,
+ object_store_id,
+ index_id,
+ MaxIDBKey(),
+ MaxIDBKey(),
+ std::numeric_limits<int64>::max());
+}
+
+int IndexDataKey::Compare(const IndexDataKey& other,
+ bool ignore_duplicates,
+ bool& ok) {
+ DCHECK_GE(database_id_, 0);
+ DCHECK_GE(object_store_id_, 0);
+ DCHECK_GE(index_id_, 0);
+ int result =
+ CompareEncodedIDBKeys(encoded_user_key_, other.encoded_user_key_, ok);
+ if (!ok || result)
+ return result;
+ if (ignore_duplicates)
+ return 0;
+ result = CompareEncodedIDBKeys(
+ encoded_primary_key_, other.encoded_primary_key_, ok);
+ if (!ok || result)
+ return result;
+ return CompareInts(sequence_number_, other.sequence_number_);
+}
+
+int64 IndexDataKey::DatabaseId() const {
+ DCHECK_GE(database_id_, 0);
+ return database_id_;
+}
+
+int64 IndexDataKey::ObjectStoreId() const {
+ DCHECK_GE(object_store_id_, 0);
+ return object_store_id_;
+}
+
+int64 IndexDataKey::IndexId() const {
+ DCHECK_GE(index_id_, 0);
+ return index_id_;
+}
+
+scoped_ptr<IndexedDBKey> IndexDataKey::user_key() const {
+ scoped_ptr<IndexedDBKey> key;
+ DecodeIDBKey(&encoded_user_key_[0],
+ &encoded_user_key_[0] + encoded_user_key_.size(),
+ &key);
+ return key.Pass();
+}
+
+scoped_ptr<IndexedDBKey> IndexDataKey::primary_key() const {
+ scoped_ptr<IndexedDBKey> key;
+ DecodeIDBKey(&encoded_primary_key_[0],
+ &encoded_primary_key_[0] + encoded_primary_key_.size(),
+ &key);
+ return key.Pass();
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.h b/content/browser/indexed_db/indexed_db_leveldb_coding.h
new file mode 100644
index 0000000..ff8f5a1
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding.h
@@ -0,0 +1,467 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_LEVELDB_CODING_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_LEVELDB_CODING_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+
+namespace content {
+
+class LevelDBSlice;
+
+CONTENT_EXPORT extern const unsigned char kMinimumIndexId;
+
+CONTENT_EXPORT std::vector<char> EncodeByte(unsigned char);
+CONTENT_EXPORT const char* DecodeByte(const char* p,
+ const char* limit,
+ unsigned char& found_char);
+CONTENT_EXPORT std::vector<char> MaxIDBKey();
+CONTENT_EXPORT std::vector<char> MinIDBKey();
+CONTENT_EXPORT std::vector<char> EncodeBool(bool value);
+CONTENT_EXPORT bool DecodeBool(const char* begin, const char* end);
+CONTENT_EXPORT std::vector<char> EncodeInt(int64);
+inline std::vector<char> EncodeIntSafely(int64 nParam, int64 max) {
+ DCHECK_LE(nParam, max);
+ return EncodeInt(nParam);
+}
+
+template <class T> int64 DecodeInt(T begin, T end) {
+ // TODO(alecflett): Make this a DCHECK_LE().
+ DCHECK(begin <= end);
+ int64 ret = 0;
+
+ int shift = 0;
+ while (begin < end) {
+ unsigned char c = *begin++;
+ ret |= static_cast<int64>(c) << shift;
+ shift += 8;
+ }
+
+ return ret;
+}
+
+CONTENT_EXPORT std::vector<char> EncodeVarInt(int64);
+CONTENT_EXPORT const char* DecodeVarInt(const char* p,
+ const char* limit,
+ int64& found_int);
+CONTENT_EXPORT std::vector<char> EncodeString(const string16& string);
+CONTENT_EXPORT string16 DecodeString(const char* p, const char* end);
+CONTENT_EXPORT std::vector<char> EncodeStringWithLength(const string16& string);
+CONTENT_EXPORT const char* DecodeStringWithLength(const char* p,
+ const char* limit,
+ string16& found_string);
+CONTENT_EXPORT int CompareEncodedStringsWithLength(const char*& p,
+ const char* limit_p,
+ const char*& q,
+ const char* limit_q,
+ bool& ok);
+CONTENT_EXPORT std::vector<char> EncodeDouble(double value);
+CONTENT_EXPORT const char* DecodeDouble(const char* p,
+ const char* limit,
+ double* value);
+void EncodeIDBKey(const IndexedDBKey& key, std::vector<char>& into);
+CONTENT_EXPORT std::vector<char> EncodeIDBKey(const IndexedDBKey& key);
+CONTENT_EXPORT const char* DecodeIDBKey(const char* p,
+ const char* limit,
+ scoped_ptr<IndexedDBKey>* found_key);
+CONTENT_EXPORT const char* ExtractEncodedIDBKey(const char* start,
+ const char* limit,
+ std::vector<char>* result);
+CONTENT_EXPORT int CompareEncodedIDBKeys(const std::vector<char>& a,
+ const std::vector<char>& b,
+ bool& ok);
+CONTENT_EXPORT std::vector<char> EncodeIDBKeyPath(
+ const IndexedDBKeyPath& key_path);
+CONTENT_EXPORT IndexedDBKeyPath DecodeIDBKeyPath(const char* start,
+ const char* limit);
+
+CONTENT_EXPORT int Compare(const LevelDBSlice& a,
+ const LevelDBSlice& b,
+ bool index_keys = false);
+
+class KeyPrefix {
+ public:
+ KeyPrefix();
+ explicit KeyPrefix(int64 database_id);
+ KeyPrefix(int64 database_id, int64 object_store_id);
+ KeyPrefix(int64 database_id, int64 object_store_id, int64 index_id);
+ static KeyPrefix CreateWithSpecialIndex(int64 database_id,
+ int64 object_store_id,
+ int64 index_id);
+
+ static const char* Decode(const char* start,
+ const char* limit,
+ KeyPrefix* result);
+ std::vector<char> Encode() const;
+ static std::vector<char> EncodeEmpty();
+ int Compare(const KeyPrefix& other) const;
+
+ enum Type {
+ GLOBAL_METADATA,
+ DATABASE_METADATA,
+ OBJECT_STORE_DATA,
+ EXISTS_ENTRY,
+ INDEX_DATA,
+ INVALID_TYPE
+ };
+
+ static const size_t kMaxDatabaseIdSizeBits = 3;
+ static const size_t kMaxObjectStoreIdSizeBits = 3;
+ static const size_t kMaxIndexIdSizeBits = 2;
+
+ static const size_t kMaxDatabaseIdSizeBytes =
+ 1ULL << kMaxDatabaseIdSizeBits; // 8
+ static const size_t kMaxObjectStoreIdSizeBytes =
+ 1ULL << kMaxObjectStoreIdSizeBits; // 8
+ static const size_t kMaxIndexIdSizeBytes = 1ULL << kMaxIndexIdSizeBits; // 4
+
+ static const size_t kMaxDatabaseIdBits =
+ kMaxDatabaseIdSizeBytes * 8 - 1; // 63
+ static const size_t kMaxObjectStoreIdBits =
+ kMaxObjectStoreIdSizeBytes * 8 - 1; // 63
+ static const size_t kMaxIndexIdBits = kMaxIndexIdSizeBytes * 8 - 1; // 31
+
+ static const int64 kMaxDatabaseId =
+ (1ULL << kMaxDatabaseIdBits) - 1; // max signed int64
+ static const int64 kMaxObjectStoreId =
+ (1ULL << kMaxObjectStoreIdBits) - 1; // max signed int64
+ static const int64 kMaxIndexId =
+ (1ULL << kMaxIndexIdBits) - 1; // max signed int32
+
+ static bool IsValidDatabaseId(int64 database_id);
+ static bool IsValidObjectStoreId(int64 index_id);
+ static bool IsValidIndexId(int64 index_id);
+ static bool ValidIds(int64 database_id,
+ int64 object_store_id,
+ int64 index_id) {
+ return IsValidDatabaseId(database_id) &&
+ IsValidObjectStoreId(object_store_id) && IsValidIndexId(index_id);
+ }
+ static bool ValidIds(int64 database_id, int64 object_store_id) {
+ return IsValidDatabaseId(database_id) &&
+ IsValidObjectStoreId(object_store_id);
+ }
+
+ Type type() const;
+
+ int64 database_id_;
+ int64 object_store_id_;
+ int64 index_id_;
+
+ static const int64 kInvalidId = -1;
+
+ private:
+ static std::vector<char> EncodeInternal(int64 database_id,
+ int64 object_store_id,
+ int64 index_id);
+ // Special constructor for CreateWithSpecialIndex()
+ KeyPrefix(enum Type,
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id);
+};
+
+class SchemaVersionKey {
+ public:
+ CONTENT_EXPORT static std::vector<char> Encode();
+};
+
+class MaxDatabaseIdKey {
+ public:
+ CONTENT_EXPORT static std::vector<char> Encode();
+};
+
+class DataVersionKey {
+ public:
+ static std::vector<char> Encode();
+};
+
+class DatabaseFreeListKey {
+ public:
+ DatabaseFreeListKey();
+ static const char* Decode(const char* start,
+ const char* limit,
+ DatabaseFreeListKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(int64 database_id);
+ static CONTENT_EXPORT std::vector<char> EncodeMaxKey();
+ int64 DatabaseId() const;
+ int Compare(const DatabaseFreeListKey& other) const;
+
+ private:
+ int64 database_id_;
+};
+
+class DatabaseNameKey {
+ public:
+ static const char* Decode(const char* start,
+ const char* limit,
+ DatabaseNameKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(const string16& origin,
+ const string16& database_name);
+ static std::vector<char> EncodeMinKeyForOrigin(const string16& origin);
+ static std::vector<char> EncodeStopKeyForOrigin(const string16& origin);
+ string16 origin() const { return origin_; }
+ string16 database_name() const { return database_name_; }
+ int Compare(const DatabaseNameKey& other);
+
+ private:
+ string16 origin_; // TODO(jsbell): Store encoded strings, or just pointers.
+ string16 database_name_;
+};
+
+class DatabaseMetaDataKey {
+ public:
+ enum MetaDataType {
+ ORIGIN_NAME = 0,
+ DATABASE_NAME = 1,
+ USER_VERSION = 2,
+ MAX_OBJECT_STORE_ID = 3,
+ USER_INT_VERSION = 4,
+ MAX_SIMPLE_METADATA_TYPE = 5
+ };
+
+ CONTENT_EXPORT static std::vector<char> Encode(int64 database_id,
+ MetaDataType type);
+};
+
+class ObjectStoreMetaDataKey {
+ public:
+ enum MetaDataType {
+ NAME = 0,
+ KEY_PATH = 1,
+ AUTO_INCREMENT = 2,
+ EVICTABLE = 3,
+ LAST_VERSION = 4,
+ MAX_INDEX_ID = 5,
+ HAS_KEY_PATH = 6,
+ KEY_GENERATOR_CURRENT_NUMBER = 7
+ };
+
+ ObjectStoreMetaDataKey();
+ static const char* Decode(const char* start,
+ const char* limit,
+ ObjectStoreMetaDataKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id,
+ unsigned char meta_data_type);
+ CONTENT_EXPORT static std::vector<char> EncodeMaxKey(int64 database_id);
+ CONTENT_EXPORT static std::vector<char> EncodeMaxKey(int64 database_id,
+ int64 object_store_id);
+ int64 ObjectStoreId() const;
+ unsigned char MetaDataType() const;
+ int Compare(const ObjectStoreMetaDataKey& other);
+
+ private:
+ int64 object_store_id_;
+ unsigned char meta_data_type_;
+};
+
+class IndexMetaDataKey {
+ public:
+ enum MetaDataType {
+ NAME = 0,
+ UNIQUE = 1,
+ KEY_PATH = 2,
+ MULTI_ENTRY = 3
+ };
+
+ IndexMetaDataKey();
+ static const char* Decode(const char* start,
+ const char* limit,
+ IndexMetaDataKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ unsigned char meta_data_type);
+ CONTENT_EXPORT static std::vector<char> EncodeMaxKey(int64 database_id,
+ int64 object_store_id);
+ CONTENT_EXPORT static std::vector<char> EncodeMaxKey(int64 database_id,
+ int64 object_store_id,
+ int64 index_id);
+ int Compare(const IndexMetaDataKey& other);
+ int64 IndexId() const;
+ unsigned char meta_data_type() const { return meta_data_type_; }
+
+ private:
+ int64 object_store_id_;
+ int64 index_id_;
+ unsigned char meta_data_type_;
+};
+
+class ObjectStoreFreeListKey {
+ public:
+ ObjectStoreFreeListKey();
+ static const char* Decode(const char* start,
+ const char* limit,
+ ObjectStoreFreeListKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id);
+ CONTENT_EXPORT static std::vector<char> EncodeMaxKey(int64 database_id);
+ int64 ObjectStoreId() const;
+ int Compare(const ObjectStoreFreeListKey& other);
+
+ private:
+ int64 object_store_id_;
+};
+
+class IndexFreeListKey {
+ public:
+ IndexFreeListKey();
+ static const char* Decode(const char* start,
+ const char* limit,
+ IndexFreeListKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id,
+ int64 index_id);
+ CONTENT_EXPORT static std::vector<char> EncodeMaxKey(int64 database_id,
+ int64 object_store_id);
+ int Compare(const IndexFreeListKey& other);
+ int64 ObjectStoreId() const;
+ int64 IndexId() const;
+
+ private:
+ int64 object_store_id_;
+ int64 index_id_;
+};
+
+class ObjectStoreNamesKey {
+ public:
+ // TODO(jsbell): We never use this to look up object store ids,
+ // because a mapping is kept in the IndexedDBDatabaseImpl. Can the
+ // mapping become unreliable? Can we remove this?
+ static const char* Decode(const char* start,
+ const char* limit,
+ ObjectStoreNamesKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(
+ int64 database_id,
+ const string16& object_store_name);
+ int Compare(const ObjectStoreNamesKey& other);
+ string16 object_store_name() const { return object_store_name_; }
+
+ private:
+ string16
+ object_store_name_; // TODO(jsbell): Store the encoded string, or just
+ // pointers to it.
+};
+
+class IndexNamesKey {
+ public:
+ IndexNamesKey();
+ // TODO(jsbell): 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);
+ CONTENT_EXPORT static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id,
+ const string16& index_name);
+ int Compare(const IndexNamesKey& other);
+ string16 index_name() const { return index_name_; }
+
+ private:
+ int64 object_store_id_;
+ string16 index_name_;
+};
+
+class ObjectStoreDataKey {
+ public:
+ static const char* Decode(const char* start,
+ const char* end,
+ ObjectStoreDataKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(
+ int64 database_id,
+ int64 object_store_id,
+ const std::vector<char> encoded_user_key);
+ static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& user_key);
+ int Compare(const ObjectStoreDataKey& other, bool& ok);
+ scoped_ptr<IndexedDBKey> user_key() const;
+ static const int64 kSpecialIndexNumber;
+ ObjectStoreDataKey();
+ ~ObjectStoreDataKey();
+
+ private:
+ std::vector<char> encoded_user_key_;
+};
+
+class ExistsEntryKey {
+ public:
+ ExistsEntryKey();
+ ~ExistsEntryKey();
+
+ static const char* Decode(const char* start,
+ const char* end,
+ ExistsEntryKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(
+ int64 database_id,
+ int64 object_store_id,
+ const std::vector<char>& encoded_key);
+ static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id,
+ const IndexedDBKey& user_key);
+ int Compare(const ExistsEntryKey& other, bool& ok);
+ scoped_ptr<IndexedDBKey> user_key() const;
+
+ static const int64 kSpecialIndexNumber;
+
+ private:
+ std::vector<char> encoded_user_key_;
+ DISALLOW_COPY_AND_ASSIGN(ExistsEntryKey);
+};
+
+class IndexDataKey {
+ public:
+ IndexDataKey();
+ ~IndexDataKey();
+ static const char* Decode(const char* start,
+ const char* limit,
+ IndexDataKey* result);
+ CONTENT_EXPORT static std::vector<char> Encode(
+ int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const std::vector<char>& encoded_user_key,
+ const std::vector<char>& encoded_primary_key,
+ int64 sequence_number = 0);
+ static std::vector<char> Encode(int64 database_id,
+ int64 object_store_id,
+ int64 index_id,
+ const IndexedDBKey& user_key);
+ static std::vector<char> EncodeMinKey(int64 database_id,
+ int64 object_store_id,
+ int64 index_id);
+ CONTENT_EXPORT static std::vector<char> EncodeMaxKey(int64 database_id,
+ int64 object_store_id,
+ int64 index_id);
+ int Compare(const IndexDataKey& other, bool ignore_duplicates, bool& ok);
+ int64 DatabaseId() const;
+ int64 ObjectStoreId() const;
+ int64 IndexId() const;
+ scoped_ptr<IndexedDBKey> user_key() const;
+ scoped_ptr<IndexedDBKey> primary_key() const;
+
+ private:
+ int64 database_id_;
+ int64 object_store_id_;
+ int64 index_id_;
+ std::vector<char> encoded_user_key_;
+ std::vector<char> encoded_primary_key_;
+ int64 sequence_number_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexDataKey);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_LEVELDB_CODING_H_
diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc b/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
new file mode 100644
index 0000000..6bbcf9a
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_leveldb_coding_unittest.cc
@@ -0,0 +1,755 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using WebKit::WebIDBKey;
+using WebKit::WebIDBKeyPath;
+
+namespace content {
+
+namespace {
+
+static IndexedDBKey CreateArrayIDBKey() {
+ return IndexedDBKey(IndexedDBKey::KeyArray());
+}
+
+static IndexedDBKey CreateArrayIDBKey(const IndexedDBKey& key1) {
+ IndexedDBKey::KeyArray array;
+ array.push_back(key1);
+ return IndexedDBKey(array);
+}
+
+static IndexedDBKey CreateArrayIDBKey(const IndexedDBKey& key1,
+ const IndexedDBKey& key2) {
+ IndexedDBKey::KeyArray array;
+ array.push_back(key1);
+ array.push_back(key2);
+ return IndexedDBKey(array);
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeByte) {
+ std::vector<char> expected;
+ expected.push_back(0);
+ unsigned char c;
+
+ c = 0;
+ expected[0] = c;
+ EXPECT_EQ(expected, EncodeByte(c));
+
+ c = 1;
+ expected[0] = c;
+ EXPECT_EQ(expected, EncodeByte(c));
+
+ c = 255;
+ expected[0] = c;
+ EXPECT_EQ(expected, EncodeByte(c));
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeByte) {
+ std::vector<unsigned char> test_cases;
+ test_cases.push_back(0);
+ test_cases.push_back(1);
+ test_cases.push_back(255);
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ unsigned char n = test_cases[i];
+ std::vector<char> v = EncodeByte(n);
+
+ unsigned char res;
+ const char* p = DecodeByte(&*v.begin(), &*v.rbegin() + 1, res);
+ EXPECT_EQ(n, res);
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeBool) {
+ {
+ std::vector<char> expected;
+ expected.push_back(1);
+ EXPECT_EQ(expected, EncodeBool(true));
+ }
+ {
+ std::vector<char> expected;
+ expected.push_back(0);
+ EXPECT_EQ(expected, EncodeBool(false));
+ }
+}
+
+static int CompareKeys(const std::vector<char>& a, const std::vector<char>& b) {
+ bool ok;
+ int result = CompareEncodedIDBKeys(a, b, ok);
+ EXPECT_TRUE(ok);
+ return result;
+}
+
+TEST(IndexedDBLevelDBCodingTest, MaxIDBKey) {
+ std::vector<char> max_key = MaxIDBKey();
+
+ std::vector<char> min_key = MinIDBKey();
+ std::vector<char> array_key =
+ EncodeIDBKey(IndexedDBKey(IndexedDBKey::KeyArray()));
+ std::vector<char> string_key =
+ EncodeIDBKey(IndexedDBKey(ASCIIToUTF16("Hello world")));
+ std::vector<char> number_key =
+ EncodeIDBKey(IndexedDBKey(3.14, WebIDBKey::NumberType));
+ std::vector<char> date_key =
+ EncodeIDBKey(IndexedDBKey(1000000, WebIDBKey::DateType));
+
+ EXPECT_GT(CompareKeys(max_key, min_key), 0);
+ EXPECT_GT(CompareKeys(max_key, array_key), 0);
+ EXPECT_GT(CompareKeys(max_key, string_key), 0);
+ EXPECT_GT(CompareKeys(max_key, number_key), 0);
+ EXPECT_GT(CompareKeys(max_key, date_key), 0);
+}
+
+TEST(IndexedDBLevelDBCodingTest, MinIDBKey) {
+ std::vector<char> min_key = MinIDBKey();
+
+ std::vector<char> max_key = MaxIDBKey();
+ std::vector<char> array_key =
+ EncodeIDBKey(IndexedDBKey(IndexedDBKey::KeyArray()));
+ std::vector<char> string_key =
+ EncodeIDBKey(IndexedDBKey(ASCIIToUTF16("Hello world")));
+ std::vector<char> number_key =
+ EncodeIDBKey(IndexedDBKey(3.14, WebIDBKey::NumberType));
+ std::vector<char> date_key =
+ EncodeIDBKey(IndexedDBKey(1000000, WebIDBKey::DateType));
+
+ EXPECT_LT(CompareKeys(min_key, max_key), 0);
+ EXPECT_LT(CompareKeys(min_key, array_key), 0);
+ EXPECT_LT(CompareKeys(min_key, string_key), 0);
+ EXPECT_LT(CompareKeys(min_key, number_key), 0);
+ EXPECT_LT(CompareKeys(min_key, date_key), 0);
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeInt) {
+ EXPECT_EQ(static_cast<size_t>(1), EncodeInt(0).size());
+ EXPECT_EQ(static_cast<size_t>(1), EncodeInt(1).size());
+ EXPECT_EQ(static_cast<size_t>(1), EncodeInt(255).size());
+ EXPECT_EQ(static_cast<size_t>(2), EncodeInt(256).size());
+ EXPECT_EQ(static_cast<size_t>(4), EncodeInt(0xffffffff).size());
+#ifdef NDEBUG
+ EXPECT_EQ(static_cast<size_t>(8), EncodeInt(-1).size());
+#endif
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeBool) {
+ {
+ std::vector<char> encoded;
+ encoded.push_back(1);
+ EXPECT_TRUE(DecodeBool(&*encoded.begin(), &*encoded.rbegin() + 1));
+ }
+ {
+ std::vector<char> encoded;
+ encoded.push_back(0);
+ EXPECT_FALSE(DecodeBool(&*encoded.begin(), &*encoded.rbegin() + 1));
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeInt) {
+ std::vector<int64> test_cases;
+ test_cases.push_back(0);
+ test_cases.push_back(1);
+ test_cases.push_back(255);
+ test_cases.push_back(256);
+ test_cases.push_back(65535);
+ test_cases.push_back(655536);
+ test_cases.push_back(7711192431755665792ll);
+ test_cases.push_back(0x7fffffffffffffffll);
+#ifdef NDEBUG
+ test_cases.push_back(-3);
+#endif
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ int64 n = test_cases[i];
+ std::vector<char> v = EncodeInt(n);
+ EXPECT_EQ(n, DecodeInt(&*v.begin(), &*v.rbegin() + 1));
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeVarInt) {
+ EXPECT_EQ(static_cast<size_t>(1), EncodeVarInt(0).size());
+ EXPECT_EQ(static_cast<size_t>(1), EncodeVarInt(1).size());
+ EXPECT_EQ(static_cast<size_t>(2), EncodeVarInt(255).size());
+ EXPECT_EQ(static_cast<size_t>(2), EncodeVarInt(256).size());
+ EXPECT_EQ(static_cast<size_t>(5), EncodeVarInt(0xffffffff).size());
+ EXPECT_EQ(static_cast<size_t>(8), EncodeVarInt(0xfffffffffffffLL).size());
+ EXPECT_EQ(static_cast<size_t>(9), EncodeVarInt(0x7fffffffffffffffLL).size());
+#ifdef NDEBUG
+ EXPECT_EQ(static_cast<size_t>(10), EncodeVarInt(-100).size());
+#endif
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeVarInt) {
+ std::vector<int64> test_cases;
+ test_cases.push_back(0);
+ test_cases.push_back(1);
+ test_cases.push_back(255);
+ test_cases.push_back(256);
+ test_cases.push_back(65535);
+ test_cases.push_back(655536);
+ test_cases.push_back(7711192431755665792ll);
+ test_cases.push_back(0x7fffffffffffffffll);
+#ifdef NDEBUG
+ test_cases.push_back(-3);
+#endif
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ int64 n = test_cases[i];
+ std::vector<char> v = EncodeVarInt(n);
+
+ int64 res;
+ const char* p = DecodeVarInt(&*v.begin(), &*v.rbegin() + 1, res);
+ EXPECT_EQ(n, res);
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+
+ p = DecodeVarInt(&*v.begin(), &*v.rbegin(), res);
+ EXPECT_EQ(0, p);
+ p = DecodeVarInt(&*v.begin(), &*v.begin(), res);
+ EXPECT_EQ(0, p);
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeString) {
+ const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+
+ EXPECT_EQ(static_cast<size_t>(0), EncodeString(ASCIIToUTF16("")).size());
+ EXPECT_EQ(static_cast<size_t>(2), EncodeString(ASCIIToUTF16("a")).size());
+ EXPECT_EQ(static_cast<size_t>(6), EncodeString(ASCIIToUTF16("foo")).size());
+ EXPECT_EQ(static_cast<size_t>(6),
+ EncodeString(string16(test_string_a)).size());
+ EXPECT_EQ(static_cast<size_t>(4),
+ EncodeString(string16(test_string_b)).size());
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeString) {
+ const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+ const char empty[] = {0};
+ std::vector<char> v;
+
+ EXPECT_EQ(string16(), DecodeString(empty, empty));
+
+ v = EncodeString(ASCIIToUTF16("a"));
+ EXPECT_EQ(ASCIIToUTF16("a"), DecodeString(&*v.begin(), &*v.rbegin() + 1));
+
+ v = EncodeString(ASCIIToUTF16("foo"));
+ EXPECT_EQ(ASCIIToUTF16("foo"), DecodeString(&*v.begin(), &*v.rbegin() + 1));
+
+ v = EncodeString(string16(test_string_a));
+ EXPECT_EQ(string16(test_string_a),
+ DecodeString(&*v.begin(), &*v.rbegin() + 1));
+
+ v = EncodeString(string16(test_string_b));
+ EXPECT_EQ(string16(test_string_b),
+ DecodeString(&*v.begin(), &*v.rbegin() + 1));
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeStringWithLength) {
+ const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+
+ EXPECT_EQ(static_cast<size_t>(1), EncodeStringWithLength(string16()).size());
+ EXPECT_EQ(static_cast<size_t>(3),
+ EncodeStringWithLength(ASCIIToUTF16("a")).size());
+ EXPECT_EQ(static_cast<size_t>(7),
+ EncodeStringWithLength(string16(test_string_a)).size());
+ EXPECT_EQ(static_cast<size_t>(5),
+ EncodeStringWithLength(string16(test_string_b)).size());
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeStringWithLength) {
+ const char16 test_string_a[] = {'f', 'o', 'o', '\0'};
+ const char16 test_string_b[] = {0xdead, 0xbeef, '\0'};
+
+ const int kLongStringLen = 1234;
+ char16 long_string[kLongStringLen + 1];
+ for (int i = 0; i < kLongStringLen; ++i)
+ long_string[i] = i;
+ long_string[kLongStringLen] = 0;
+
+ std::vector<string16> test_cases;
+ test_cases.push_back(ASCIIToUTF16(""));
+ test_cases.push_back(ASCIIToUTF16("a"));
+ test_cases.push_back(ASCIIToUTF16("foo"));
+ test_cases.push_back(string16(test_string_a));
+ test_cases.push_back(string16(test_string_b));
+ test_cases.push_back(string16(long_string));
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ string16 s = test_cases[i];
+ std::vector<char> v = EncodeStringWithLength(s);
+ string16 res;
+ const char* p = DecodeStringWithLength(&*v.begin(), &*v.rbegin() + 1, res);
+ EXPECT_EQ(s, res);
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+
+ EXPECT_EQ(0, DecodeStringWithLength(&*v.begin(), &*v.rbegin(), res));
+ EXPECT_EQ(0, DecodeStringWithLength(&*v.begin(), &*v.begin(), res));
+ }
+}
+
+static int CompareStrings(const char* p,
+ const char* limit_p,
+ const char* q,
+ const char* limit_q) {
+ bool ok;
+ int result = CompareEncodedStringsWithLength(p, limit_p, q, limit_q, ok);
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(p, limit_p);
+ EXPECT_EQ(q, limit_q);
+ return result;
+}
+
+TEST(IndexedDBLevelDBCodingTest, CompareEncodedStringsWithLength) {
+ const char16 test_string_a[] = {0x1000, 0x1000, '\0'};
+ const char16 test_string_b[] = {0x1000, 0x1000, 0x1000, '\0'};
+ const char16 test_string_c[] = {0x1000, 0x1000, 0x1001, '\0'};
+ const char16 test_string_d[] = {0x1001, 0x1000, 0x1000, '\0'};
+ const char16 test_string_e[] = {0xd834, 0xdd1e, '\0'};
+ const char16 test_string_f[] = {0xfffd, '\0'};
+
+ std::vector<string16> test_cases;
+ test_cases.push_back(ASCIIToUTF16(""));
+ test_cases.push_back(ASCIIToUTF16("a"));
+ test_cases.push_back(ASCIIToUTF16("b"));
+ test_cases.push_back(ASCIIToUTF16("baaa"));
+ test_cases.push_back(ASCIIToUTF16("baab"));
+ test_cases.push_back(ASCIIToUTF16("c"));
+ test_cases.push_back(string16(test_string_a));
+ test_cases.push_back(string16(test_string_b));
+ test_cases.push_back(string16(test_string_c));
+ test_cases.push_back(string16(test_string_d));
+ test_cases.push_back(string16(test_string_e));
+ test_cases.push_back(string16(test_string_f));
+
+ for (size_t i = 0; i < test_cases.size() - 1; ++i) {
+ string16 a = test_cases[i];
+ string16 b = test_cases[i + 1];
+
+ EXPECT_LT(a.compare(b), 0);
+ EXPECT_GT(b.compare(a), 0);
+ EXPECT_EQ(a.compare(a), 0);
+ EXPECT_EQ(b.compare(b), 0);
+
+ std::vector<char> encoded_a = EncodeStringWithLength(a);
+ EXPECT_TRUE(encoded_a.size());
+ std::vector<char> encoded_b = EncodeStringWithLength(b);
+ EXPECT_TRUE(encoded_a.size());
+
+ const char* p = &*encoded_a.begin();
+ const char* limit_p = &*encoded_a.rbegin() + 1;
+ const char* q = &*encoded_b.begin();
+ const char* limit_q = &*encoded_b.rbegin() + 1;
+
+ EXPECT_LT(CompareStrings(p, limit_p, q, limit_q), 0);
+ EXPECT_GT(CompareStrings(q, limit_q, p, limit_p), 0);
+ EXPECT_EQ(CompareStrings(p, limit_p, p, limit_p), 0);
+ EXPECT_EQ(CompareStrings(q, limit_q, q, limit_q), 0);
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeDouble) {
+ EXPECT_EQ(static_cast<size_t>(8), EncodeDouble(0).size());
+ EXPECT_EQ(static_cast<size_t>(8), EncodeDouble(3.14).size());
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeDouble) {
+ std::vector<char> v;
+ const char* p;
+ double d;
+
+ v = EncodeDouble(3.14);
+ p = DecodeDouble(&*v.begin(), &*v.rbegin() + 1, &d);
+ EXPECT_EQ(3.14, d);
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+
+ v = EncodeDouble(-3.14);
+ p = DecodeDouble(&*v.begin(), &*v.rbegin() + 1, &d);
+ EXPECT_EQ(-3.14, d);
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+
+ v = EncodeDouble(3.14);
+ p = DecodeDouble(&*v.begin(), &*v.rbegin(), &d);
+ EXPECT_EQ(0, p);
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeDecodeIDBKey) {
+ IndexedDBKey expected_key;
+ scoped_ptr<IndexedDBKey> decoded_key;
+ std::vector<char> v;
+ const char* p;
+
+ expected_key = IndexedDBKey(1234, WebIDBKey::NumberType);
+ v = EncodeIDBKey(expected_key);
+ p = DecodeIDBKey(&*v.begin(), &*v.rbegin() + 1, &decoded_key);
+ EXPECT_TRUE(decoded_key->IsEqual(expected_key));
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+ EXPECT_EQ(0, DecodeIDBKey(&*v.begin(), &*v.rbegin(), &decoded_key));
+
+ expected_key = IndexedDBKey(ASCIIToUTF16("Hello World!"));
+ v = EncodeIDBKey(expected_key);
+ p = DecodeIDBKey(&*v.begin(), &*v.rbegin() + 1, &decoded_key);
+ EXPECT_TRUE(decoded_key->IsEqual(expected_key));
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+ EXPECT_EQ(0, DecodeIDBKey(&*v.begin(), &*v.rbegin(), &decoded_key));
+
+ expected_key = IndexedDBKey(IndexedDBKey::KeyArray());
+ v = EncodeIDBKey(expected_key);
+ p = DecodeIDBKey(&*v.begin(), &*v.rbegin() + 1, &decoded_key);
+ EXPECT_TRUE(decoded_key->IsEqual(expected_key));
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+ EXPECT_EQ(0, DecodeIDBKey(&*v.begin(), &*v.rbegin(), &decoded_key));
+
+ expected_key = IndexedDBKey(7890, WebIDBKey::DateType);
+ v = EncodeIDBKey(expected_key);
+ p = DecodeIDBKey(&*v.begin(), &*v.rbegin() + 1, &decoded_key);
+ EXPECT_TRUE(decoded_key->IsEqual(expected_key));
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+ EXPECT_EQ(0, DecodeIDBKey(&*v.begin(), &*v.rbegin(), &decoded_key));
+
+ IndexedDBKey::KeyArray array;
+ array.push_back(IndexedDBKey(1234, WebIDBKey::NumberType));
+ array.push_back(IndexedDBKey(ASCIIToUTF16("Hello World!")));
+ array.push_back(IndexedDBKey(7890, WebIDBKey::DateType));
+ expected_key = IndexedDBKey(array);
+ v = EncodeIDBKey(expected_key);
+ p = DecodeIDBKey(&*v.begin(), &*v.rbegin() + 1, &decoded_key);
+ EXPECT_TRUE(decoded_key->IsEqual(expected_key));
+ EXPECT_EQ(&*v.rbegin() + 1, p);
+ EXPECT_EQ(0, DecodeIDBKey(&*v.begin(), &*v.rbegin(), &decoded_key));
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeIDBKeyPath) {
+ const unsigned char kIDBKeyPathTypeCodedByte1 = 0;
+ const unsigned char kIDBKeyPathTypeCodedByte2 = 0;
+ {
+ IndexedDBKeyPath key_path;
+ EXPECT_EQ(key_path.type(), WebIDBKeyPath::NullType);
+ std::vector<char> v = EncodeIDBKeyPath(key_path);
+ EXPECT_EQ(v.size(), 3U);
+ EXPECT_EQ(v[0], kIDBKeyPathTypeCodedByte1);
+ EXPECT_EQ(v[1], kIDBKeyPathTypeCodedByte2);
+ EXPECT_EQ(v[2], WebIDBKeyPath::NullType);
+ }
+
+ {
+ std::vector<string16> test_cases;
+ test_cases.push_back(string16());
+ test_cases.push_back(ASCIIToUTF16("foo"));
+ test_cases.push_back(ASCIIToUTF16("foo.bar"));
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ IndexedDBKeyPath key_path = IndexedDBKeyPath(test_cases[i]);
+ std::vector<char> v = EncodeIDBKeyPath(key_path);
+ EXPECT_EQ(v.size(), EncodeStringWithLength(test_cases[i]).size() + 3);
+ const char* p = &*v.begin();
+ const char* limit = &*v.rbegin() + 1;
+ EXPECT_EQ(*p++, kIDBKeyPathTypeCodedByte1);
+ EXPECT_EQ(*p++, kIDBKeyPathTypeCodedByte2);
+ EXPECT_EQ(*p++, WebIDBKeyPath::StringType);
+ string16 string;
+ p = DecodeStringWithLength(p, limit, string);
+ EXPECT_EQ(string, test_cases[i]);
+ EXPECT_EQ(p, limit);
+ }
+ }
+
+ {
+ std::vector<string16> test_case;
+ test_case.push_back(string16());
+ test_case.push_back(ASCIIToUTF16("foo"));
+ test_case.push_back(ASCIIToUTF16("foo.bar"));
+
+ IndexedDBKeyPath key_path(test_case);
+ EXPECT_EQ(key_path.type(), WebIDBKeyPath::ArrayType);
+ std::vector<char> v = EncodeIDBKeyPath(key_path);
+ const char* p = &*v.begin();
+ const char* limit = &*v.rbegin() + 1;
+ EXPECT_EQ(*p++, kIDBKeyPathTypeCodedByte1);
+ EXPECT_EQ(*p++, kIDBKeyPathTypeCodedByte2);
+ EXPECT_EQ(*p++, WebIDBKeyPath::ArrayType);
+ int64 count;
+ p = DecodeVarInt(p, limit, count);
+ EXPECT_EQ(count, static_cast<int64>(test_case.size()));
+ for (size_t i = 0; i < static_cast<size_t>(count); ++i) {
+ string16 string;
+ p = DecodeStringWithLength(p, limit, string);
+ EXPECT_EQ(string, test_case[i]);
+ }
+ EXPECT_EQ(p, limit);
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, DecodeIDBKeyPath) {
+ const unsigned char kIDBKeyPathTypeCodedByte1 = 0;
+ const unsigned char kIDBKeyPathTypeCodedByte2 = 0;
+ const char empty[] = {0};
+ {
+ // Legacy encoding of string key paths.
+ std::vector<string16> test_cases;
+ test_cases.push_back(string16());
+ test_cases.push_back(ASCIIToUTF16("foo"));
+ test_cases.push_back(ASCIIToUTF16("foo.bar"));
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ std::vector<char> v = EncodeString(test_cases[i]);
+ const char* begin;
+ const char* end;
+ if (!v.size()) {
+ begin = empty;
+ end = empty;
+ } else {
+ begin = &*v.begin();
+ end = &*v.rbegin() + 1;
+ }
+ IndexedDBKeyPath key_path = DecodeIDBKeyPath(begin, end);
+ EXPECT_EQ(key_path.type(), WebIDBKeyPath::StringType);
+ EXPECT_EQ(test_cases[i], key_path.string());
+ }
+ }
+ {
+ std::vector<char> v;
+ v.push_back(kIDBKeyPathTypeCodedByte1);
+ v.push_back(kIDBKeyPathTypeCodedByte2);
+ v.push_back(WebIDBKeyPath::NullType);
+ IndexedDBKeyPath key_path = DecodeIDBKeyPath(&*v.begin(), &*v.rbegin() + 1);
+ EXPECT_EQ(key_path.type(), WebIDBKeyPath::NullType);
+ EXPECT_TRUE(key_path.IsNull());
+ }
+ {
+ std::vector<string16> test_cases;
+ test_cases.push_back(string16());
+ test_cases.push_back(ASCIIToUTF16("foo"));
+ test_cases.push_back(ASCIIToUTF16("foo.bar"));
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ std::vector<char> v;
+ v.push_back(kIDBKeyPathTypeCodedByte1);
+ v.push_back(kIDBKeyPathTypeCodedByte2);
+ v.push_back(WebIDBKeyPath::StringType);
+ std::vector<char> test_case = EncodeStringWithLength(test_cases[i]);
+ v.insert(v.end(), test_case.begin(), test_case.end());
+ IndexedDBKeyPath key_path =
+ DecodeIDBKeyPath(&*v.begin(), &*v.rbegin() + 1);
+ EXPECT_EQ(key_path.type(), WebIDBKeyPath::StringType);
+ EXPECT_EQ(test_cases[i], key_path.string());
+ }
+ }
+ {
+ std::vector<string16> test_case;
+ test_case.push_back(string16());
+ test_case.push_back(ASCIIToUTF16("foo"));
+ test_case.push_back(ASCIIToUTF16("foo.bar"));
+
+ std::vector<char> v;
+ v.push_back(kIDBKeyPathTypeCodedByte1);
+ v.push_back(kIDBKeyPathTypeCodedByte2);
+ v.push_back(WebIDBKeyPath::ArrayType);
+ std::vector<char> int_value = EncodeVarInt(test_case.size());
+ v.insert(v.end(), int_value.begin(), int_value.end());
+ for (size_t i = 0; i < test_case.size(); ++i) {
+ std::vector<char> test_case_value = EncodeStringWithLength(test_case[i]);
+ v.insert(v.end(), test_case_value.begin(), test_case_value.end());
+ }
+ IndexedDBKeyPath key_path = DecodeIDBKeyPath(&*v.begin(), &*v.rbegin() + 1);
+ EXPECT_EQ(key_path.type(), WebIDBKeyPath::ArrayType);
+ EXPECT_EQ(key_path.array().size(), test_case.size());
+ for (size_t i = 0; i < test_case.size(); ++i)
+ EXPECT_EQ(key_path.array()[i], test_case[i]);
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, ExtractAndCompareIDBKeys) {
+ std::vector<IndexedDBKey> keys;
+
+ keys.push_back(IndexedDBKey(-10, WebIDBKey::NumberType));
+ keys.push_back(IndexedDBKey(0, WebIDBKey::NumberType));
+ keys.push_back(IndexedDBKey(3.14, WebIDBKey::NumberType));
+
+ keys.push_back(IndexedDBKey(0, WebIDBKey::DateType));
+ keys.push_back(IndexedDBKey(100, WebIDBKey::DateType));
+ keys.push_back(IndexedDBKey(100000, WebIDBKey::DateType));
+
+ keys.push_back(IndexedDBKey(ASCIIToUTF16("")));
+ keys.push_back(IndexedDBKey(ASCIIToUTF16("a")));
+ keys.push_back(IndexedDBKey(ASCIIToUTF16("b")));
+ keys.push_back(IndexedDBKey(ASCIIToUTF16("baaa")));
+ keys.push_back(IndexedDBKey(ASCIIToUTF16("baab")));
+ keys.push_back(IndexedDBKey(ASCIIToUTF16("c")));
+
+ keys.push_back(CreateArrayIDBKey());
+ keys.push_back(CreateArrayIDBKey(IndexedDBKey(0, WebIDBKey::NumberType)));
+ keys.push_back(CreateArrayIDBKey(IndexedDBKey(0, WebIDBKey::NumberType),
+ IndexedDBKey(3.14, WebIDBKey::NumberType)));
+ keys.push_back(CreateArrayIDBKey(IndexedDBKey(0, WebIDBKey::DateType)));
+ keys.push_back(CreateArrayIDBKey(IndexedDBKey(0, WebIDBKey::DateType),
+ IndexedDBKey(0, WebIDBKey::DateType)));
+ keys.push_back(CreateArrayIDBKey(IndexedDBKey(ASCIIToUTF16(""))));
+ keys.push_back(CreateArrayIDBKey(IndexedDBKey(ASCIIToUTF16("")),
+ IndexedDBKey(ASCIIToUTF16("a"))));
+ keys.push_back(CreateArrayIDBKey(CreateArrayIDBKey()));
+ keys.push_back(CreateArrayIDBKey(CreateArrayIDBKey(), CreateArrayIDBKey()));
+ keys.push_back(CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey())));
+ keys.push_back(CreateArrayIDBKey(
+ CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey()))));
+
+ for (size_t i = 0; i < keys.size() - 1; ++i) {
+ const IndexedDBKey& key_a = keys[i];
+ const IndexedDBKey& key_b = keys[i + 1];
+
+ EXPECT_TRUE(key_a.IsLessThan(key_b));
+
+ std::vector<char> encoded_a = EncodeIDBKey(key_a);
+ EXPECT_TRUE(encoded_a.size());
+ std::vector<char> encoded_b = EncodeIDBKey(key_b);
+ EXPECT_TRUE(encoded_b.size());
+
+ std::vector<char> extracted_a;
+ std::vector<char> extracted_b;
+
+ const char* p = ExtractEncodedIDBKey(
+ &*encoded_a.begin(), &*encoded_a.rbegin() + 1, &extracted_a);
+ EXPECT_EQ(&*encoded_a.rbegin() + 1, p);
+ EXPECT_EQ(encoded_a, extracted_a);
+
+ const char* q = ExtractEncodedIDBKey(
+ &*encoded_b.begin(), &*encoded_b.rbegin() + 1, &extracted_b);
+ EXPECT_EQ(&*encoded_b.rbegin() + 1, q);
+ EXPECT_EQ(encoded_b, extracted_b);
+
+ EXPECT_LT(CompareKeys(extracted_a, extracted_b), 0);
+ EXPECT_GT(CompareKeys(extracted_b, extracted_a), 0);
+ EXPECT_EQ(CompareKeys(extracted_a, extracted_a), 0);
+ EXPECT_EQ(CompareKeys(extracted_b, extracted_b), 0);
+
+ EXPECT_EQ(0,
+ ExtractEncodedIDBKey(
+ &*encoded_a.begin(), &*encoded_a.rbegin(), &extracted_a));
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, ComparisonTest) {
+ std::vector<std::vector<char> > keys;
+ keys.push_back(SchemaVersionKey::Encode());
+ keys.push_back(MaxDatabaseIdKey::Encode());
+ keys.push_back(DatabaseFreeListKey::Encode(0));
+ keys.push_back(DatabaseFreeListKey::EncodeMaxKey());
+ keys.push_back(DatabaseNameKey::Encode(ASCIIToUTF16(""), ASCIIToUTF16("")));
+ keys.push_back(DatabaseNameKey::Encode(ASCIIToUTF16(""), ASCIIToUTF16("a")));
+ keys.push_back(DatabaseNameKey::Encode(ASCIIToUTF16("a"), ASCIIToUTF16("a")));
+ keys.push_back(
+ DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::ORIGIN_NAME));
+ keys.push_back(
+ DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::DATABASE_NAME));
+ keys.push_back(
+ DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_VERSION));
+ keys.push_back(
+ DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID));
+ keys.push_back(
+ DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_INT_VERSION));
+ keys.push_back(
+ ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::NAME));
+ keys.push_back(
+ ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::KEY_PATH));
+ keys.push_back(ObjectStoreMetaDataKey::Encode(
+ 1, 1, ObjectStoreMetaDataKey::AUTO_INCREMENT));
+ keys.push_back(
+ ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::EVICTABLE));
+ keys.push_back(ObjectStoreMetaDataKey::Encode(
+ 1, 1, ObjectStoreMetaDataKey::LAST_VERSION));
+ keys.push_back(ObjectStoreMetaDataKey::Encode(
+ 1, 1, ObjectStoreMetaDataKey::MAX_INDEX_ID));
+ keys.push_back(ObjectStoreMetaDataKey::Encode(
+ 1, 1, ObjectStoreMetaDataKey::HAS_KEY_PATH));
+ keys.push_back(ObjectStoreMetaDataKey::Encode(
+ 1, 1, ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER));
+ keys.push_back(ObjectStoreMetaDataKey::EncodeMaxKey(1, 1));
+ keys.push_back(ObjectStoreMetaDataKey::EncodeMaxKey(1, 2));
+ keys.push_back(ObjectStoreMetaDataKey::EncodeMaxKey(1));
+ keys.push_back(IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::NAME));
+ keys.push_back(IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::UNIQUE));
+ keys.push_back(
+ IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::KEY_PATH));
+ keys.push_back(
+ IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::MULTI_ENTRY));
+ keys.push_back(IndexMetaDataKey::Encode(1, 1, 31, 0));
+ keys.push_back(IndexMetaDataKey::Encode(1, 1, 31, 1));
+ keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 1, 31));
+ keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 1, 32));
+ keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 1));
+ keys.push_back(IndexMetaDataKey::EncodeMaxKey(1, 2));
+ keys.push_back(ObjectStoreFreeListKey::Encode(1, 1));
+ keys.push_back(ObjectStoreFreeListKey::EncodeMaxKey(1));
+ keys.push_back(IndexFreeListKey::Encode(1, 1, kMinimumIndexId));
+ keys.push_back(IndexFreeListKey::EncodeMaxKey(1, 1));
+ keys.push_back(IndexFreeListKey::Encode(1, 2, kMinimumIndexId));
+ keys.push_back(IndexFreeListKey::EncodeMaxKey(1, 2));
+ keys.push_back(ObjectStoreNamesKey::Encode(1, ASCIIToUTF16("")));
+ keys.push_back(ObjectStoreNamesKey::Encode(1, ASCIIToUTF16("a")));
+ keys.push_back(IndexNamesKey::Encode(1, 1, ASCIIToUTF16("")));
+ keys.push_back(IndexNamesKey::Encode(1, 1, ASCIIToUTF16("a")));
+ keys.push_back(IndexNamesKey::Encode(1, 2, ASCIIToUTF16("a")));
+ keys.push_back(ObjectStoreDataKey::Encode(1, 1, MinIDBKey()));
+ keys.push_back(ObjectStoreDataKey::Encode(1, 1, MaxIDBKey()));
+ keys.push_back(ExistsEntryKey::Encode(1, 1, MinIDBKey()));
+ keys.push_back(ExistsEntryKey::Encode(1, 1, MaxIDBKey()));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 1));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 0));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 1));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 0));
+ keys.push_back(IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 1));
+ keys.push_back(IndexDataKey::Encode(1, 1, 31, MinIDBKey(), MinIDBKey(), 0));
+ keys.push_back(IndexDataKey::Encode(1, 2, 30, MinIDBKey(), MinIDBKey(), 0));
+ keys.push_back(
+ IndexDataKey::EncodeMaxKey(1, 2, std::numeric_limits<int32>::max() - 1));
+
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const LevelDBSlice key_a(keys[i]);
+ EXPECT_EQ(Compare(key_a, key_a), 0);
+
+ for (size_t j = i + 1; j < keys.size(); ++j) {
+ const LevelDBSlice key_b(keys[j]);
+ EXPECT_LT(Compare(key_a, key_b), 0);
+ EXPECT_GT(Compare(key_b, key_a), 0);
+ }
+ }
+}
+
+TEST(IndexedDBLevelDBCodingTest, EncodeVarIntVSEncodeByteTest) {
+ std::vector<unsigned char> test_cases;
+ test_cases.push_back(0);
+ test_cases.push_back(1);
+ test_cases.push_back(127);
+
+ for (size_t i = 0; i < test_cases.size(); ++i) {
+ unsigned char n = test_cases[i];
+
+ std::vector<char> vA = EncodeByte(n);
+ std::vector<char> vB = EncodeVarInt(static_cast<int64>(n));
+
+ EXPECT_EQ(vA.size(), vB.size());
+ EXPECT_EQ(*vA.begin(), *vB.begin());
+ }
+}
+
+} // namespace
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_metadata.cc b/content/browser/indexed_db/indexed_db_metadata.cc
new file mode 100644
index 0000000..80d3142
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_metadata.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_metadata.h"
+
+namespace content {
+
+IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata(
+ const string16& name,
+ int64 id,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment,
+ int64 max_index_id)
+ : name(name),
+ id(id),
+ key_path(key_path),
+ auto_increment(auto_increment),
+ max_index_id(max_index_id) {}
+
+IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata() {}
+IndexedDBObjectStoreMetadata::~IndexedDBObjectStoreMetadata() {}
+
+IndexedDBDatabaseMetadata::IndexedDBDatabaseMetadata()
+ : int_version(NO_INT_VERSION) {}
+IndexedDBDatabaseMetadata::IndexedDBDatabaseMetadata(const string16& name,
+ int64 id,
+ const string16& version,
+ int64 int_version,
+ int64 max_object_store_id)
+ : name(name),
+ id(id),
+ version(version),
+ int_version(int_version),
+ max_object_store_id(max_object_store_id) {}
+
+IndexedDBDatabaseMetadata::~IndexedDBDatabaseMetadata() {}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_metadata.h b/content/browser/indexed_db/indexed_db_metadata.h
new file mode 100644
index 0000000..29f6a9f
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_metadata.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_METADATA_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_METADATA_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "content/common/indexed_db/indexed_db_key_path.h"
+
+namespace content {
+
+struct IndexedDBIndexMetadata {
+ IndexedDBIndexMetadata() {}
+ IndexedDBIndexMetadata(const string16& name,
+ int64 id,
+ const IndexedDBKeyPath& key_path,
+ bool unique,
+ bool multi_entry)
+ : name(name),
+ id(id),
+ key_path(key_path),
+ unique(unique),
+ multi_entry(multi_entry) {}
+ string16 name;
+ int64 id;
+ IndexedDBKeyPath key_path;
+ bool unique;
+ bool multi_entry;
+
+ static const int64 kInvalidId = -1;
+};
+
+struct CONTENT_EXPORT IndexedDBObjectStoreMetadata {
+ IndexedDBObjectStoreMetadata();
+ IndexedDBObjectStoreMetadata(const string16& name,
+ int64 id,
+ const IndexedDBKeyPath& key_path,
+ bool auto_increment,
+ int64 max_index_id);
+ ~IndexedDBObjectStoreMetadata();
+ string16 name;
+ int64 id;
+ IndexedDBKeyPath key_path;
+ bool auto_increment;
+ int64 max_index_id;
+
+ static const int64 kInvalidId = -1;
+
+ typedef std::map<int64, IndexedDBIndexMetadata> IndexMap;
+ IndexMap indexes;
+};
+
+struct CONTENT_EXPORT IndexedDBDatabaseMetadata {
+ // TODO(jsbell): These can probably be collapsed into 0.
+ enum {
+ NO_INT_VERSION = -1,
+ DEFAULT_INT_VERSION = 0
+ };
+
+ typedef std::map<int64, IndexedDBObjectStoreMetadata> ObjectStoreMap;
+
+ IndexedDBDatabaseMetadata();
+ IndexedDBDatabaseMetadata(const string16& name,
+ int64 id,
+ const string16& version,
+ int64 int_version,
+ int64 max_object_store_id);
+ ~IndexedDBDatabaseMetadata();
+
+ string16 name;
+ int64 id;
+ string16 version;
+ int64 int_version;
+ int64 max_object_store_id;
+
+ ObjectStoreMap object_stores;
+};
+}
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_METADATA_H_
diff --git a/content/browser/indexed_db/indexed_db_quota_client.cc b/content/browser/indexed_db/indexed_db_quota_client.cc
index 25f9668..bbce16c 100644
--- a/content/browser/indexed_db/indexed_db_quota_client.cc
+++ b/content/browser/indexed_db/indexed_db_quota_client.cc
@@ -26,36 +26,33 @@ quota::QuotaStatusCode DeleteOriginDataOnWebKitThread(
return quota::kQuotaStatusOk;
}
-int64 GetOriginUsageOnWebKitThread(
- IndexedDBContextImpl* context,
- const GURL& origin) {
+int64 GetOriginUsageOnWebKitThread(IndexedDBContextImpl* context,
+ const GURL& origin) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
return context->GetOriginDiskUsage(origin);
}
-void GetAllOriginsOnWebKitThread(
- IndexedDBContextImpl* context,
- std::set<GURL>* origins_to_return) {
+void GetAllOriginsOnWebKitThread(IndexedDBContextImpl* context,
+ std::set<GURL>* origins_to_return) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
std::vector<GURL> all_origins = context->GetAllOrigins();
origins_to_return->insert(all_origins.begin(), all_origins.end());
}
-void DidGetOrigins(
- const IndexedDBQuotaClient::GetOriginsCallback& callback,
- const std::set<GURL>* origins) {
+void DidGetOrigins(const IndexedDBQuotaClient::GetOriginsCallback& callback,
+ const std::set<GURL>* origins) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
callback.Run(*origins);
}
-void GetOriginsForHostOnWebKitThread(
- IndexedDBContextImpl* context,
- const std::string& host,
- std::set<GURL>* origins_to_return) {
+void GetOriginsForHostOnWebKitThread(IndexedDBContextImpl* context,
+ const std::string& host,
+ std::set<GURL>* origins_to_return) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
std::vector<GURL> all_origins = context->GetAllOrigins();
for (std::vector<GURL>::const_iterator iter = all_origins.begin();
- iter != all_origins.end(); ++iter) {
+ iter != all_origins.end();
+ ++iter) {
if (host == net::GetHostOrSpecFromURL(*iter))
origins_to_return->insert(*iter);
}
@@ -69,24 +66,17 @@ IndexedDBQuotaClient::IndexedDBQuotaClient(
base::MessageLoopProxy* webkit_thread_message_loop,
IndexedDBContextImpl* indexed_db_context)
: webkit_thread_message_loop_(webkit_thread_message_loop),
- indexed_db_context_(indexed_db_context) {
-}
+ indexed_db_context_(indexed_db_context) {}
-IndexedDBQuotaClient::~IndexedDBQuotaClient() {
-}
+IndexedDBQuotaClient::~IndexedDBQuotaClient() {}
-QuotaClient::ID IndexedDBQuotaClient::id() const {
- return kIndexedDatabase;
-}
+QuotaClient::ID IndexedDBQuotaClient::id() const { return kIndexedDatabase; }
-void IndexedDBQuotaClient::OnQuotaManagerDestroyed() {
- delete this;
-}
+void IndexedDBQuotaClient::OnQuotaManagerDestroyed() { delete this; }
-void IndexedDBQuotaClient::GetOriginUsage(
- const GURL& origin_url,
- quota::StorageType type,
- const GetUsageCallback& callback) {
+void IndexedDBQuotaClient::GetOriginUsage(const GURL& origin_url,
+ quota::StorageType type,
+ const GetUsageCallback& callback) {
DCHECK(!callback.is_null());
DCHECK(indexed_db_context_.get());
@@ -99,9 +89,8 @@ void IndexedDBQuotaClient::GetOriginUsage(
base::PostTaskAndReplyWithResult(
webkit_thread_message_loop_,
FROM_HERE,
- base::Bind(&GetOriginUsageOnWebKitThread,
- indexed_db_context_,
- origin_url),
+ base::Bind(
+ &GetOriginUsageOnWebKitThread, indexed_db_context_, origin_url),
callback);
}
@@ -123,9 +112,7 @@ void IndexedDBQuotaClient::GetOriginsForType(
base::Bind(&GetAllOriginsOnWebKitThread,
indexed_db_context_,
base::Unretained(origins_to_return)),
- base::Bind(&DidGetOrigins,
- callback,
- base::Owned(origins_to_return)));
+ base::Bind(&DidGetOrigins, callback, base::Owned(origins_to_return)));
}
void IndexedDBQuotaClient::GetOriginsForHost(
@@ -148,15 +135,12 @@ void IndexedDBQuotaClient::GetOriginsForHost(
indexed_db_context_,
host,
base::Unretained(origins_to_return)),
- base::Bind(&DidGetOrigins,
- callback,
- base::Owned(origins_to_return)));
+ base::Bind(&DidGetOrigins, callback, base::Owned(origins_to_return)));
}
-void IndexedDBQuotaClient::DeleteOriginData(
- const GURL& origin,
- quota::StorageType type,
- const DeletionCallback& callback) {
+void IndexedDBQuotaClient::DeleteOriginData(const GURL& origin,
+ quota::StorageType type,
+ const DeletionCallback& callback) {
if (type != quota::kStorageTypeTemporary) {
callback.Run(quota::kQuotaErrorNotSupported);
return;
@@ -165,9 +149,7 @@ void IndexedDBQuotaClient::DeleteOriginData(
base::PostTaskAndReplyWithResult(
webkit_thread_message_loop_,
FROM_HERE,
- base::Bind(&DeleteOriginDataOnWebKitThread,
- indexed_db_context_,
- origin),
+ base::Bind(&DeleteOriginDataOnWebKitThread, indexed_db_context_, origin),
callback);
}
diff --git a/content/browser/indexed_db/indexed_db_quota_client.h b/content/browser/indexed_db/indexed_db_quota_client.h
index e79f4aa..605e705 100644
--- a/content/browser/indexed_db/indexed_db_quota_client.h
+++ b/content/browser/indexed_db/indexed_db_quota_client.h
@@ -25,9 +25,8 @@ class IndexedDBContextImpl;
class IndexedDBQuotaClient : public quota::QuotaClient,
public quota::QuotaTaskObserver {
public:
- CONTENT_EXPORT IndexedDBQuotaClient(
- base::MessageLoopProxy* tracker_thread,
- IndexedDBContextImpl* indexed_db_context);
+ CONTENT_EXPORT IndexedDBQuotaClient(base::MessageLoopProxy* tracker_thread,
+ IndexedDBContextImpl* indexed_db_context);
CONTENT_EXPORT virtual ~IndexedDBQuotaClient();
// QuotaClient method overrides
@@ -44,6 +43,7 @@ class IndexedDBQuotaClient : public quota::QuotaClient,
virtual void DeleteOriginData(const GURL& origin,
quota::StorageType type,
const DeletionCallback& callback) OVERRIDE;
+
private:
scoped_refptr<base::MessageLoopProxy> webkit_thread_message_loop_;
scoped_refptr<IndexedDBContextImpl> indexed_db_context_;
diff --git a/content/browser/indexed_db/indexed_db_quota_client_unittest.cc b/content/browser/indexed_db/indexed_db_quota_client_unittest.cc
index 0d8628ac..bcd037a 100644
--- a/content/browser/indexed_db/indexed_db_quota_client_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_quota_client_unittest.cc
@@ -48,15 +48,15 @@ class IndexedDBQuotaClientTest : public testing::Test {
io_thread_(BrowserThread::IO, &message_loop_) {
browser_context_.reset(new TestBrowserContext());
idb_context_ = static_cast<IndexedDBContextImpl*>(
- BrowserContext::GetDefaultStoragePartition(browser_context_.get())->
- GetIndexedDBContext());
+ BrowserContext::GetDefaultStoragePartition(browser_context_.get())
+ ->GetIndexedDBContext());
message_loop_.RunUntilIdle();
setup_temp_dir();
}
void setup_temp_dir() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- base::FilePath indexeddb_dir = temp_dir_.path().Append(
- IndexedDBContextImpl::kIndexedDBDirectory);
+ base::FilePath indexeddb_dir =
+ temp_dir_.path().Append(IndexedDBContextImpl::kIndexedDBDirectory);
ASSERT_TRUE(file_util::CreateDirectory(indexeddb_dir));
idb_context()->set_data_path_for_testing(indexeddb_dir);
}
@@ -71,13 +71,13 @@ class IndexedDBQuotaClientTest : public testing::Test {
base::MessageLoop::current()->RunUntilIdle();
}
- int64 GetOriginUsage(
- quota::QuotaClient* client,
- const GURL& origin,
- quota::StorageType type) {
+ int64 GetOriginUsage(quota::QuotaClient* client,
+ const GURL& origin,
+ quota::StorageType type) {
usage_ = -1;
client->GetOriginUsage(
- origin, type,
+ origin,
+ type,
base::Bind(&IndexedDBQuotaClientTest::OnGetOriginUsageComplete,
weak_factory_.GetWeakPtr()));
base::MessageLoop::current()->RunUntilIdle();
@@ -85,9 +85,8 @@ class IndexedDBQuotaClientTest : public testing::Test {
return usage_;
}
- const std::set<GURL>& GetOriginsForType(
- quota::QuotaClient* client,
- quota::StorageType type) {
+ const std::set<GURL>& GetOriginsForType(quota::QuotaClient* client,
+ quota::StorageType type) {
origins_.clear();
client->GetOriginsForType(
type,
@@ -97,13 +96,13 @@ class IndexedDBQuotaClientTest : public testing::Test {
return origins_;
}
- const std::set<GURL>& GetOriginsForHost(
- quota::QuotaClient* client,
- quota::StorageType type,
- const std::string& host) {
+ const std::set<GURL>& GetOriginsForHost(quota::QuotaClient* client,
+ quota::StorageType type,
+ const std::string& host) {
origins_.clear();
client->GetOriginsForHost(
- type, host,
+ type,
+ host,
base::Bind(&IndexedDBQuotaClientTest::OnGetOriginsComplete,
weak_factory_.GetWeakPtr()));
base::MessageLoop::current()->RunUntilIdle();
@@ -114,7 +113,8 @@ class IndexedDBQuotaClientTest : public testing::Test {
const GURL& origin_url) {
delete_status_ = quota::kQuotaStatusUnknown;
client->DeleteOriginData(
- origin_url, kTemp,
+ origin_url,
+ kTemp,
base::Bind(&IndexedDBQuotaClientTest::OnDeleteOriginComplete,
weak_factory_.GetWeakPtr()));
base::MessageLoop::current()->RunUntilIdle();
@@ -141,9 +141,7 @@ class IndexedDBQuotaClientTest : public testing::Test {
}
private:
- void OnGetOriginUsageComplete(int64 usage) {
- usage_ = usage;
- }
+ void OnGetOriginUsageComplete(int64 usage) { usage_ = usage; }
void OnGetOriginsComplete(const std::set<GURL>& origins) {
origins_ = origins;
@@ -169,9 +167,7 @@ class IndexedDBQuotaClientTest : public testing::Test {
};
TEST_F(IndexedDBQuotaClientTest, GetOriginUsage) {
- IndexedDBQuotaClient client(
- base::MessageLoopProxy::current(),
- idb_context());
+ IndexedDBQuotaClient client(base::MessageLoopProxy::current(), idb_context());
AddFakeIndexedDB(kOriginA, 6);
AddFakeIndexedDB(kOriginB, 3);
@@ -188,9 +184,7 @@ TEST_F(IndexedDBQuotaClientTest, GetOriginUsage) {
}
TEST_F(IndexedDBQuotaClientTest, GetOriginsForHost) {
- IndexedDBQuotaClient client(
- base::MessageLoopProxy::current(),
- idb_context());
+ IndexedDBQuotaClient client(base::MessageLoopProxy::current(), idb_context());
EXPECT_EQ(kOriginA.host(), kOriginB.host());
EXPECT_NE(kOriginA.host(), kOriginOther.host());
@@ -214,9 +208,7 @@ TEST_F(IndexedDBQuotaClientTest, GetOriginsForHost) {
}
TEST_F(IndexedDBQuotaClientTest, GetOriginsForType) {
- IndexedDBQuotaClient client(
- base::MessageLoopProxy::current(),
- idb_context());
+ IndexedDBQuotaClient client(base::MessageLoopProxy::current(), idb_context());
EXPECT_TRUE(GetOriginsForType(&client, kTemp).empty());
EXPECT_TRUE(GetOriginsForType(&client, kPerm).empty());
@@ -230,9 +222,7 @@ TEST_F(IndexedDBQuotaClientTest, GetOriginsForType) {
}
TEST_F(IndexedDBQuotaClientTest, DeleteOrigin) {
- IndexedDBQuotaClient client(
- base::MessageLoopProxy::current(),
- idb_context());
+ IndexedDBQuotaClient client(base::MessageLoopProxy::current(), idb_context());
AddFakeIndexedDB(kOriginA, 1000);
AddFakeIndexedDB(kOriginB, 50);
diff --git a/content/browser/indexed_db/indexed_db_tracing.h b/content/browser/indexed_db/indexed_db_tracing.h
new file mode 100644
index 0000000..86ddaa34b
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_tracing.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRACING_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRACING_H_
+
+#include "base/debug/trace_event.h"
+#define IDB_TRACE(a) TRACE_EVENT0("IndexedDB", (a));
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRACING_H_
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
new file mode 100644
index 0000000..b0a9946
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -0,0 +1,308 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_transaction.h"
+
+#include <vector>
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_cursor_impl.h"
+#include "content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_database_impl.h"
+#include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
+
+namespace content {
+
+IndexedDBTransaction::TaskQueue::TaskQueue() {}
+IndexedDBTransaction::TaskQueue::~TaskQueue() { clear(); }
+
+void IndexedDBTransaction::TaskQueue::clear() {
+ while (!queue_.empty())
+ scoped_ptr<Operation> task(pop());
+}
+
+scoped_ptr<IndexedDBTransaction::Operation>
+IndexedDBTransaction::TaskQueue::pop() {
+ DCHECK(!queue_.empty());
+ scoped_ptr<Operation> task(queue_.front());
+ queue_.pop();
+ return task.Pass();
+}
+
+IndexedDBTransaction::TaskStack::TaskStack() {}
+IndexedDBTransaction::TaskStack::~TaskStack() { clear(); }
+
+void IndexedDBTransaction::TaskStack::clear() {
+ while (!stack_.empty())
+ scoped_ptr<Operation> task(pop());
+}
+
+scoped_ptr<IndexedDBTransaction::Operation>
+IndexedDBTransaction::TaskStack::pop() {
+ DCHECK(!stack_.empty());
+ scoped_ptr<Operation> task(stack_.top());
+ stack_.pop();
+ return task.Pass();
+}
+
+scoped_refptr<IndexedDBTransaction> IndexedDBTransaction::Create(
+ int64 id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::vector<int64>& object_store_ids,
+ indexed_db::TransactionMode mode,
+ IndexedDBDatabaseImpl* database) {
+ std::set<int64> object_store_hash_set;
+ for (size_t i = 0; i < object_store_ids.size(); ++i)
+ object_store_hash_set.insert(object_store_ids[i]);
+
+ return make_scoped_refptr(new IndexedDBTransaction(
+ id, callbacks, object_store_hash_set, mode, database));
+}
+
+IndexedDBTransaction::IndexedDBTransaction(
+ int64 id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::set<int64>& object_store_ids,
+ indexed_db::TransactionMode mode,
+ IndexedDBDatabaseImpl* database)
+ : id_(id),
+ object_store_ids_(object_store_ids),
+ mode_(mode),
+ state_(UNUSED),
+ commit_pending_(false),
+ callbacks_(callbacks),
+ database_(database),
+ transaction_(database->BackingStore().get()),
+ pending_preemptive_events_(0) {
+ database_->transaction_coordinator().DidCreateTransaction(this);
+}
+
+IndexedDBTransaction::~IndexedDBTransaction() {
+ // It shouldn't be possible for this object to get deleted until it's either
+ // complete or aborted.
+ DCHECK_EQ(state_, FINISHED);
+ DCHECK(preemptive_task_queue_.empty());
+ DCHECK(task_queue_.empty());
+ DCHECK(abort_task_stack_.empty());
+}
+
+void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
+ Operation* task,
+ Operation* abort_task) {
+ if (state_ == FINISHED)
+ return;
+
+ if (type == IndexedDBDatabase::NORMAL_TASK)
+ task_queue_.push(task);
+ else
+ preemptive_task_queue_.push(task);
+
+ if (abort_task)
+ abort_task_stack_.push(abort_task);
+
+ if (state_ == UNUSED)
+ Start();
+ else if (state_ == RUNNING && !task_timer_.IsRunning())
+ task_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(0),
+ this,
+ &IndexedDBTransaction::TaskTimerFired);
+}
+
+void IndexedDBTransaction::Abort() {
+ Abort(IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error (unknown cause)")));
+}
+
+void IndexedDBTransaction::Abort(scoped_refptr<IndexedDBDatabaseError> error) {
+ IDB_TRACE("IndexedDBTransaction::abort");
+ if (state_ == FINISHED)
+ return;
+
+ bool was_running = state_ == RUNNING;
+
+ // The last reference to this object may be released while performing the
+ // abort steps below. We therefore take a self reference to keep ourselves
+ // alive while executing this method.
+ scoped_refptr<IndexedDBTransaction> protect(this);
+
+ state_ = FINISHED;
+ task_timer_.Stop();
+
+ if (was_running)
+ transaction_.Rollback();
+
+ // Run the abort tasks, if any.
+ while (!abort_task_stack_.empty()) {
+ scoped_ptr<Operation> task(abort_task_stack_.pop());
+ task->Perform(0);
+ }
+ preemptive_task_queue_.clear();
+ task_queue_.clear();
+
+ // Backing store resources (held via cursors) must be released
+ // before script callbacks are fired, as the script callbacks may
+ // release references and allow the backing store itself to be
+ // released, and order is critical.
+ CloseOpenCursors();
+ transaction_.Reset();
+
+ // Transactions must also be marked as completed before the
+ // front-end is notified, as the transaction completion unblocks
+ // operations like closing connections.
+ database_->transaction_coordinator().DidFinishTransaction(this);
+#ifndef NDEBUG
+ DCHECK(!database_->transaction_coordinator().IsActive(this));
+#endif
+ database_->TransactionFinished(this);
+
+ if (callbacks_)
+ callbacks_->OnAbort(id_, error);
+
+ database_->TransactionFinishedAndAbortFired(this);
+
+ database_ = NULL;
+}
+
+bool IndexedDBTransaction::IsTaskQueueEmpty() const {
+ return preemptive_task_queue_.empty() && task_queue_.empty();
+}
+
+bool IndexedDBTransaction::HasPendingTasks() const {
+ return pending_preemptive_events_ || !IsTaskQueueEmpty();
+}
+
+void IndexedDBTransaction::RegisterOpenCursor(IndexedDBCursorImpl* cursor) {
+ open_cursors_.insert(cursor);
+}
+
+void IndexedDBTransaction::UnregisterOpenCursor(IndexedDBCursorImpl* cursor) {
+ open_cursors_.erase(cursor);
+}
+
+void IndexedDBTransaction::Run() {
+ // TransactionCoordinator has started this transaction. Schedule a timer
+ // to process the first task.
+ DCHECK(state_ == START_PENDING || state_ == RUNNING);
+ DCHECK(!task_timer_.IsRunning());
+
+ task_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(0),
+ this,
+ &IndexedDBTransaction::TaskTimerFired);
+}
+
+void IndexedDBTransaction::Start() {
+ DCHECK_EQ(state_, UNUSED);
+
+ state_ = START_PENDING;
+ database_->transaction_coordinator().DidStartTransaction(this);
+ database_->TransactionStarted(this);
+}
+
+void IndexedDBTransaction::Commit() {
+ IDB_TRACE("IndexedDBTransaction::commit");
+
+ // In multiprocess ports, front-end may have requested a commit but
+ // an abort has already been initiated asynchronously by the
+ // back-end.
+ if (state_ == FINISHED)
+ return;
+
+ DCHECK(state_ == UNUSED || state_ == RUNNING);
+ commit_pending_ = true;
+
+ // Front-end has requested a commit, but there may be tasks like
+ // create_index which are considered synchronous by the front-end
+ // but are processed asynchronously.
+ if (HasPendingTasks())
+ return;
+
+ // The last reference to this object may be released while performing the
+ // commit steps below. We therefore take a self reference to keep ourselves
+ // alive while executing this method.
+ scoped_refptr<IndexedDBTransaction> protect(this);
+
+ // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843
+ abort_task_stack_.clear();
+
+ bool unused = state_ == UNUSED;
+ state_ = FINISHED;
+
+ bool committed = unused || transaction_.Commit();
+
+ // Backing store resources (held via cursors) must be released
+ // before script callbacks are fired, as the script callbacks may
+ // release references and allow the backing store itself to be
+ // released, and order is critical.
+ CloseOpenCursors();
+ transaction_.Reset();
+
+ // Transactions must also be marked as completed before the
+ // front-end is notified, as the transaction completion unblocks
+ // operations like closing connections.
+ if (!unused)
+ database_->transaction_coordinator().DidFinishTransaction(this);
+ database_->TransactionFinished(this);
+
+ if (committed) {
+ callbacks_->OnComplete(id_);
+ database_->TransactionFinishedAndCompleteFired(this);
+ } else {
+ callbacks_->OnAbort(
+ id_,
+ IndexedDBDatabaseError::Create(
+ WebKit::WebIDBDatabaseExceptionUnknownError,
+ ASCIIToUTF16("Internal error committing transaction.")));
+ database_->TransactionFinishedAndAbortFired(this);
+ }
+
+ database_ = NULL;
+}
+
+void IndexedDBTransaction::TaskTimerFired() {
+ IDB_TRACE("IndexedDBTransaction::task_timer_fired");
+ DCHECK(!IsTaskQueueEmpty());
+
+ if (state_ == START_PENDING) {
+ transaction_.begin();
+ state_ = RUNNING;
+ }
+
+ // The last reference to this object may be released while performing the
+ // tasks. Take take a self reference to keep this object alive so that
+ // the loop termination conditions can be checked.
+ scoped_refptr<IndexedDBTransaction> protect(this);
+
+ TaskQueue* task_queue =
+ pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
+ while (!task_queue->empty() && state_ != FINISHED) {
+ DCHECK_EQ(state_, RUNNING);
+ scoped_ptr<Operation> task(task_queue->pop());
+ task->Perform(this);
+
+ // Event itself may change which queue should be processed next.
+ task_queue =
+ pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
+ }
+
+ // If there are no pending tasks, we haven't already committed/aborted,
+ // and the front-end requested a commit, it is now safe to do so.
+ if (!HasPendingTasks() && state_ != FINISHED && commit_pending_)
+ Commit();
+}
+
+void IndexedDBTransaction::CloseOpenCursors() {
+ for (std::set<IndexedDBCursorImpl*>::iterator i = open_cursors_.begin();
+ i != open_cursors_.end();
+ ++i)
+ (*i)->Close();
+ open_cursors_.clear();
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_transaction.h b/content/browser/indexed_db/indexed_db_transaction.h
new file mode 100644
index 0000000..ca0f929
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_transaction.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_H_
+
+#include <queue>
+#include <set>
+#include <stack>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/timer.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_database.h"
+#include "content/browser/indexed_db/indexed_db_database_error.h"
+
+namespace content {
+
+class IndexedDBDatabaseImpl;
+class IndexedDBCursorImpl;
+class IndexedDBDatabaseCallbacksWrapper;
+
+class IndexedDBTransaction : public base::RefCounted<IndexedDBTransaction> {
+ public:
+ static scoped_refptr<IndexedDBTransaction> Create(
+ int64 transaction_id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::vector<int64>& scope,
+ indexed_db::TransactionMode,
+ IndexedDBDatabaseImpl* db);
+
+ virtual void Abort();
+ void Commit();
+
+ class Operation {
+ public:
+ Operation() {}
+ virtual ~Operation() {}
+ virtual void Perform(IndexedDBTransaction* transaction) = 0;
+ };
+
+ void Abort(scoped_refptr<IndexedDBDatabaseError> error);
+ void Run();
+ indexed_db::TransactionMode mode() const { return mode_; }
+ const std::set<int64>& scope() const { return object_store_ids_; }
+ void ScheduleTask(Operation* task, Operation* abort_task = NULL) {
+ ScheduleTask(IndexedDBDatabase::NORMAL_TASK, task, abort_task);
+ }
+ void ScheduleTask(IndexedDBDatabase::TaskType,
+ Operation* task,
+ Operation* abort_task = NULL);
+ void RegisterOpenCursor(IndexedDBCursorImpl* cursor);
+ void UnregisterOpenCursor(IndexedDBCursorImpl* cursor);
+ void AddPreemptiveEvent() { pending_preemptive_events_++; }
+ void DidCompletePreemptiveEvent() {
+ pending_preemptive_events_--;
+ DCHECK_GE(pending_preemptive_events_, 0);
+ }
+ IndexedDBBackingStore::Transaction* BackingStoreTransaction() {
+ return &transaction_;
+ }
+ int64 id() const { return id_; }
+
+ IndexedDBDatabaseImpl* database() const { return database_.get(); }
+ IndexedDBDatabaseCallbacksWrapper* connection() const {
+ return callbacks_.get();
+ }
+
+ protected:
+ virtual ~IndexedDBTransaction();
+ friend class base::RefCounted<IndexedDBTransaction>;
+
+ private:
+ IndexedDBTransaction(
+ int64 id,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
+ const std::set<int64>& object_store_ids,
+ indexed_db::TransactionMode,
+ IndexedDBDatabaseImpl* db);
+
+ enum State {
+ UNUSED, // Created, but no tasks yet.
+ START_PENDING, // Enqueued tasks, but backing store transaction not yet
+ // started.
+ RUNNING, // Backing store transaction started but not yet finished.
+ FINISHED, // Either aborted or committed.
+ };
+
+ void Start();
+
+ bool IsTaskQueueEmpty() const;
+ bool HasPendingTasks() const;
+
+ void TaskTimerFired();
+ void CloseOpenCursors();
+
+ const int64 id_;
+ const std::set<int64> object_store_ids_;
+ const indexed_db::TransactionMode mode_;
+
+ State state_;
+ bool commit_pending_;
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks_;
+ scoped_refptr<IndexedDBDatabaseImpl> database_;
+
+ class TaskQueue {
+ public:
+ TaskQueue();
+ ~TaskQueue();
+ bool empty() const { return queue_.empty(); }
+ void push(Operation* task) { queue_.push(task); }
+ scoped_ptr<Operation> pop();
+ void clear();
+
+ private:
+ std::queue<Operation*> queue_;
+ };
+
+ class TaskStack {
+ public:
+ TaskStack();
+ ~TaskStack();
+ bool empty() const { return stack_.empty(); }
+ void push(Operation* task) { stack_.push(task); }
+ scoped_ptr<Operation> pop();
+ void clear();
+
+ private:
+ std::stack<Operation*> stack_;
+ };
+
+ TaskQueue task_queue_;
+ TaskQueue preemptive_task_queue_;
+ TaskStack abort_task_stack_;
+
+ IndexedDBBackingStore::Transaction transaction_;
+
+ base::OneShotTimer<IndexedDBTransaction> task_timer_;
+ int pending_preemptive_events_;
+
+ std::set<IndexedDBCursorImpl*> open_cursors_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_H_
diff --git a/content/browser/indexed_db/indexed_db_transaction_coordinator.cc b/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
new file mode 100644
index 0000000..06d6fa0
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
+
+namespace content {
+
+IndexedDBTransactionCoordinator::IndexedDBTransactionCoordinator() {}
+
+IndexedDBTransactionCoordinator::~IndexedDBTransactionCoordinator() {
+ // TODO(jsbell): DCHECK that all queues are empty. http://crbug.com/241821
+}
+
+void IndexedDBTransactionCoordinator::DidCreateTransaction(
+ IndexedDBTransaction* transaction) {
+ DCHECK(transactions_.find(transaction) == transactions_.end());
+ transactions_[transaction] = transaction;
+}
+
+void IndexedDBTransactionCoordinator::DidStartTransaction(
+ IndexedDBTransaction* transaction) {
+ DCHECK(transactions_.find(transaction) != transactions_.end());
+
+ queued_transactions_.insert(transaction);
+ ProcessStartedTransactions();
+}
+
+void IndexedDBTransactionCoordinator::DidFinishTransaction(
+ IndexedDBTransaction* transaction) {
+ DCHECK(transactions_.find(transaction) != transactions_.end());
+
+ if (queued_transactions_.has(transaction)) {
+ DCHECK(started_transactions_.find(transaction) ==
+ started_transactions_.end());
+ queued_transactions_.erase(transaction);
+ } else {
+ if (started_transactions_.find(transaction) != started_transactions_.end())
+ started_transactions_.erase(transaction);
+ }
+ transactions_.erase(transaction);
+
+ ProcessStartedTransactions();
+}
+
+#ifndef NDEBUG
+// Verifies internal consistency while returning whether anything is found.
+bool IndexedDBTransactionCoordinator::IsActive(
+ IndexedDBTransaction* transaction) {
+ bool found = false;
+ if (queued_transactions_.has(transaction))
+ found = true;
+ if (started_transactions_.find(transaction) != started_transactions_.end()) {
+ DCHECK(!found);
+ found = true;
+ }
+ DCHECK_EQ(found, (transactions_.find(transaction) != transactions_.end()));
+ return found;
+}
+#endif
+
+void IndexedDBTransactionCoordinator::ProcessStartedTransactions() {
+ if (queued_transactions_.empty())
+ return;
+
+ DCHECK(started_transactions_.empty() ||
+ (*started_transactions_.begin())->mode() !=
+ indexed_db::TRANSACTION_VERSION_CHANGE);
+
+ list_set<IndexedDBTransaction*>::const_iterator it =
+ queued_transactions_.begin();
+ while (it != queued_transactions_.end()) {
+ IndexedDBTransaction* transaction = *it;
+ ++it;
+ if (CanRunTransaction(transaction)) {
+ queued_transactions_.erase(transaction);
+ started_transactions_.insert(transaction);
+ transaction->Run();
+ }
+ }
+}
+
+static bool DoScopesOverlap(const std::set<int64>& scope1,
+ const std::set<int64>& scope2) {
+ for (std::set<int64>::const_iterator it = scope1.begin(); it != scope1.end();
+ ++it) {
+ if (scope2.find(*it) != scope2.end())
+ return true;
+ }
+ return false;
+}
+
+bool IndexedDBTransactionCoordinator::CanRunTransaction(
+ IndexedDBTransaction* transaction) {
+ DCHECK(queued_transactions_.has(transaction));
+ switch (transaction->mode()) {
+ case indexed_db::TRANSACTION_VERSION_CHANGE:
+ DCHECK(queued_transactions_.size() == 1);
+ DCHECK(started_transactions_.empty());
+ return true;
+
+ case indexed_db::TRANSACTION_READ_ONLY:
+ return true;
+
+ case indexed_db::TRANSACTION_READ_WRITE:
+ for (std::set<IndexedDBTransaction*>::const_iterator it =
+ started_transactions_.begin();
+ it != started_transactions_.end();
+ ++it) {
+ IndexedDBTransaction* other = *it;
+ if (other->mode() == indexed_db::TRANSACTION_READ_WRITE &&
+ DoScopesOverlap(transaction->scope(), other->scope()))
+ return false;
+ }
+ for (list_set<IndexedDBTransaction*>::const_iterator it =
+ queued_transactions_.begin();
+ *it != transaction;
+ ++it) {
+ DCHECK(it != queued_transactions_.end());
+ IndexedDBTransaction* other = *it;
+ if (other->mode() == indexed_db::TRANSACTION_READ_WRITE &&
+ DoScopesOverlap(transaction->scope(), other->scope()))
+ return false;
+ }
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_transaction_coordinator.h b/content/browser/indexed_db/indexed_db_transaction_coordinator.h
new file mode 100644
index 0000000..1e01e9e
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_transaction_coordinator.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_COORDINATOR_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_COORDINATOR_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/indexed_db/list_set.h"
+
+namespace content {
+
+class IndexedDBTransaction;
+
+// Transactions are executed in the order the were created.
+class IndexedDBTransactionCoordinator {
+ public:
+ IndexedDBTransactionCoordinator();
+ ~IndexedDBTransactionCoordinator();
+
+ // Called by transactions as they start and finish.
+ void DidCreateTransaction(IndexedDBTransaction* transaction);
+ void DidStartTransaction(IndexedDBTransaction* transaction);
+ void DidFinishTransaction(IndexedDBTransaction* transaction);
+
+#ifndef NDEBUG
+ bool IsActive(IndexedDBTransaction* transaction);
+#endif
+
+ // TODO(jsbell): API to handle closed connections. http://crbug.com/241821
+
+ private:
+ void ProcessStartedTransactions();
+ bool CanRunTransaction(IndexedDBTransaction* transaction);
+
+ // This is just an efficient way to keep references to all transactions.
+ std::map<IndexedDBTransaction*, scoped_refptr<IndexedDBTransaction> >
+ transactions_;
+ // Transactions in different states are grouped below.
+ list_set<IndexedDBTransaction*> queued_transactions_;
+ std::set<IndexedDBTransaction*> started_transactions_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_TRANSACTION_COORDINATOR_H_
diff --git a/content/browser/indexed_db/indexed_db_unittest.cc b/content/browser/indexed_db/indexed_db_unittest.cc
index d4a9fb9..9e90b8a 100644
--- a/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -55,10 +55,9 @@ TEST_F(IndexedDBTest, ClearSessionOnlyDatabases) {
// Create some indexedDB paths.
// With the levelDB backend, these are directories.
- IndexedDBContextImpl* idb_context =
- static_cast<IndexedDBContextImpl*>(
- BrowserContext::GetDefaultStoragePartition(&browser_context)->
- GetIndexedDBContext());
+ IndexedDBContextImpl* idb_context = static_cast<IndexedDBContextImpl*>(
+ BrowserContext::GetDefaultStoragePartition(&browser_context)
+ ->GetIndexedDBContext());
// Override the storage policy with our own.
idb_context->special_storage_policy_ = special_storage_policy;
@@ -99,10 +98,9 @@ TEST_F(IndexedDBTest, SetForceKeepSessionState) {
// Create some indexedDB paths.
// With the levelDB backend, these are directories.
- IndexedDBContextImpl* idb_context =
- static_cast<IndexedDBContextImpl*>(
- BrowserContext::GetDefaultStoragePartition(&browser_context)->
- GetIndexedDBContext());
+ IndexedDBContextImpl* idb_context = static_cast<IndexedDBContextImpl*>(
+ BrowserContext::GetDefaultStoragePartition(&browser_context)
+ ->GetIndexedDBContext());
// Override the storage policy with our own.
idb_context->special_storage_policy_ = special_storage_policy;
@@ -128,20 +126,16 @@ TEST_F(IndexedDBTest, SetForceKeepSessionState) {
EXPECT_TRUE(file_util::DirectoryExists(session_only_path));
}
-class MockWebIDBDatabase : public WebKit::WebIDBDatabase
-{
+class MockWebIDBDatabase : public WebKit::WebIDBDatabase {
public:
MockWebIDBDatabase(bool expect_force_close)
- : expect_force_close_(expect_force_close),
- force_close_called_(false) {}
+ : expect_force_close_(expect_force_close), force_close_called_(false) {}
- virtual ~MockWebIDBDatabase()
- {
+ virtual ~MockWebIDBDatabase() {
EXPECT_TRUE(force_close_called_ == expect_force_close_);
}
- virtual void forceClose()
- {
+ virtual void forceClose() {
ASSERT_TRUE(expect_force_close_);
force_close_called_ = true;
}
@@ -151,7 +145,6 @@ class MockWebIDBDatabase : public WebKit::WebIDBDatabase
bool force_close_called_;
};
-
TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
@@ -165,10 +158,9 @@ TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
const GURL kTestOrigin("http://test/");
- IndexedDBContextImpl* idb_context =
- static_cast<IndexedDBContextImpl*>(
- BrowserContext::GetDefaultStoragePartition(&browser_context)->
- GetIndexedDBContext());
+ IndexedDBContextImpl* idb_context = static_cast<IndexedDBContextImpl*>(
+ BrowserContext::GetDefaultStoragePartition(&browser_context)
+ ->GetIndexedDBContext());
idb_context->quota_manager_proxy_ = NULL;
idb_context->set_data_path_for_testing(temp_dir.path());
diff --git a/content/browser/indexed_db/leveldb/avltree.h b/content/browser/indexed_db/leveldb/avltree.h
new file mode 100644
index 0000000..104d69b
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/avltree.h
@@ -0,0 +1,979 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Based on Abstract AVL Tree Template v1.5 by Walt Karas
+ * <http://geocities.com/wkaras/gen_cpp/avl_tree.html>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_AVLTREE_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_AVLTREE_H_
+
+#include "base/logging.h"
+#include "content/browser/indexed_db/leveldb/fixed_array.h"
+
+namespace content {
+
+// Here is the reference class for BSet.
+//
+// class BSet
+// {
+// public:
+//
+// class ANY_bitref
+// {
+// public:
+// operator bool ();
+// void operator = (bool b);
+// };
+//
+// // Does not have to initialize bits.
+// BSet();
+//
+// // Must return a valid value for index when 0 <= index < maxDepth
+// ANY_bitref operator [] (unsigned index);
+//
+// // Set all bits to 1.
+// void set();
+//
+// // Set all bits to 0.
+// void reset();
+// };
+
+template <unsigned maxDepth> class AVLTreeDefaultBSet {
+ public:
+ bool& operator[](unsigned i) {
+#if defined(ADDRESS_SANITIZER)
+ CHECK(i < maxDepth);
+#endif
+ return m_data[i];
+ }
+ void set() {
+ for (unsigned i = 0; i < maxDepth; ++i)
+ m_data[i] = true;
+ }
+ void reset() {
+ for (unsigned i = 0; i < maxDepth; ++i)
+ m_data[i] = false;
+ }
+
+ private:
+ FixedArray<bool, maxDepth> m_data;
+};
+
+// How to determine maxDepth:
+// d Minimum number of nodes
+// 2 2
+// 3 4
+// 4 7
+// 5 12
+// 6 20
+// 7 33
+// 8 54
+// 9 88
+// 10 143
+// 11 232
+// 12 376
+// 13 609
+// 14 986
+// 15 1,596
+// 16 2,583
+// 17 4,180
+// 18 6,764
+// 19 10,945
+// 20 17,710
+// 21 28,656
+// 22 46,367
+// 23 75,024
+// 24 121,392
+// 25 196,417
+// 26 317,810
+// 27 514,228
+// 28 832,039
+// 29 1,346,268
+// 30 2,178,308
+// 31 3,524,577
+// 32 5,702,886
+// 33 9,227,464
+// 34 14,930,351
+// 35 24,157,816
+// 36 39,088,168
+// 37 63,245,985
+// 38 102,334,154
+// 39 165,580,140
+// 40 267,914,295
+// 41 433,494,436
+// 42 701,408,732
+// 43 1,134,903,169
+// 44 1,836,311,902
+// 45 2,971,215,072
+//
+// E.g., if, in a particular instantiation, the maximum number of nodes in a
+// tree instance is 1,000,000, the maximum depth should be 28.
+// You pick 28 because MN(28) is 832,039, which is less than or equal to
+// 1,000,000, and MN(29) is 1,346,268, which is strictly greater than 1,000,000.
+
+template <class Abstractor,
+ unsigned maxDepth = 32,
+ class BSet = AVLTreeDefaultBSet<maxDepth> >
+class AVLTree {
+ public:
+ typedef typename Abstractor::key key;
+ typedef typename Abstractor::handle handle;
+ typedef typename Abstractor::size size;
+
+ enum SearchType {
+ EQUAL = 1,
+ LESS = 2,
+ GREATER = 4,
+ LESS_EQUAL = EQUAL | LESS,
+ GREATER_EQUAL = EQUAL | GREATER
+ };
+
+ Abstractor& abstractor() { return abs; }
+
+ inline handle insert(handle h);
+
+ inline handle search(key k, SearchType st = EQUAL);
+ inline handle search_least();
+ inline handle search_greatest();
+
+ inline handle remove(key k);
+
+ inline handle subst(handle new_node);
+
+ void purge() { abs.root = null(); }
+
+ bool is_empty() { return abs.root == null(); }
+
+ AVLTree() { abs.root = null(); }
+
+ class Iterator {
+ public:
+ // Initialize depth to invalid value, to indicate iterator is
+ // invalid. (Depth is zero-base.)
+ Iterator() { depth = ~0U; }
+
+ void start_iter(AVLTree& tree, key k, SearchType st = EQUAL) {
+ // Mask of high bit in an int.
+ const int kMaskHighBit = (int) ~((~(unsigned) 0) >> 1);
+
+ // Save the tree that we're going to iterate through in a
+ // member variable.
+ tree_ = &tree;
+
+ int cmp, target_cmp;
+ handle h = tree_->abs.root;
+ unsigned d = 0;
+
+ depth = ~0U;
+
+ if (h == null()) {
+ // Tree is empty.
+ return;
+ }
+
+ if (st & LESS) {
+ // Key can be greater than key of starting node.
+ target_cmp = 1;
+ } else if (st & GREATER) {
+ // Key can be less than key of starting node.
+ target_cmp = -1;
+ } else {
+ // Key must be same as key of starting node.
+ target_cmp = 0;
+ }
+
+ for (;;) {
+ cmp = cmp_k_n(k, h);
+ if (cmp == 0) {
+ if (st & EQUAL) {
+ // Equal node was sought and found as starting node.
+ depth = d;
+ break;
+ }
+ cmp = -target_cmp;
+ } else if (target_cmp != 0) {
+ if (!((cmp ^ target_cmp) & kMaskHighBit)) {
+ // cmp and target_cmp are both negative or both positive.
+ depth = d;
+ }
+ }
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ if (h == null())
+ break;
+ branch[d] = cmp > 0;
+ path_h[d++] = h;
+ }
+ }
+
+ void start_iter_least(AVLTree& tree) {
+ tree_ = &tree;
+
+ handle h = tree_->abs.root;
+
+ depth = ~0U;
+
+ branch.reset();
+
+ while (h != null()) {
+ if (depth != ~0U)
+ path_h[depth] = h;
+ depth++;
+ h = get_lt(h);
+ }
+ }
+
+ void start_iter_greatest(AVLTree& tree) {
+ tree_ = &tree;
+
+ handle h = tree_->abs.root;
+
+ depth = ~0U;
+
+ branch.set();
+
+ while (h != null()) {
+ if (depth != ~0U)
+ path_h[depth] = h;
+ depth++;
+ h = get_gt(h);
+ }
+ }
+
+ handle operator*() {
+ if (depth == ~0U)
+ return null();
+
+ return depth == 0 ? tree_->abs.root : path_h[depth - 1];
+ }
+
+ void operator++() {
+ if (depth != ~0U) {
+ handle h = get_gt(**this);
+ if (h == null()) {
+ do {
+ if (depth == 0) {
+ depth = ~0U;
+ break;
+ }
+ depth--;
+ } while (branch[depth]);
+ } else {
+ branch[depth] = true;
+ path_h[depth++] = h;
+ for (;;) {
+ h = get_lt(h);
+ if (h == null())
+ break;
+ branch[depth] = false;
+ path_h[depth++] = h;
+ }
+ }
+ }
+ }
+
+ void operator--() {
+ if (depth != ~0U) {
+ handle h = get_lt(**this);
+ if (h == null()) {
+ do {
+ if (depth == 0) {
+ depth = ~0U;
+ break;
+ }
+ depth--;
+ } while (!branch[depth]);
+ } else {
+ branch[depth] = false;
+ path_h[depth++] = h;
+ for (;;) {
+ h = get_gt(h);
+ if (h == null())
+ break;
+ branch[depth] = true;
+ path_h[depth++] = h;
+ }
+ }
+ }
+ }
+
+ void operator++(int) { ++(*this); }
+ void operator--(int) { --(*this); }
+
+ protected:
+
+ // Tree being iterated over.
+ AVLTree* tree_;
+
+ // Records a path into the tree. If branch[n] is true, indicates
+ // take greater branch from the nth node in the path, otherwise
+ // take the less branch. branch[0] gives branch from root, and
+ // so on.
+ BSet branch;
+
+ // Zero-based depth of path into tree.
+ unsigned depth;
+
+ // Handles of nodes in path from root to current node (returned by *).
+ handle path_h[maxDepth - 1];
+
+ int cmp_k_n(key k, handle h) { return tree_->abs.compare_key_node(k, h); }
+ int cmp_n_n(handle h1, handle h2) {
+ return tree_->abs.compare_node_node(h1, h2);
+ }
+ handle get_lt(handle h) { return tree_->abs.get_less(h); }
+ handle get_gt(handle h) { return tree_->abs.get_greater(h); }
+ handle null() { return tree_->abs.null(); }
+ };
+
+ template <typename fwd_iter> bool build(fwd_iter p, size num_nodes) {
+ if (num_nodes == 0) {
+ abs.root = null();
+ return true;
+ }
+
+ // Gives path to subtree being built. If branch[N] is false, branch
+ // less from the node at depth N, if true branch greater.
+ BSet branch;
+
+ // If rem[N] is true, then for the current subtree at depth N, it's
+ // greater subtree has one more node than it's less subtree.
+ BSet rem;
+
+ // Depth of root node of current subtree.
+ unsigned depth = 0;
+
+ // Number of nodes in current subtree.
+ size num_sub = num_nodes;
+
+ // The algorithm relies on a stack of nodes whose less subtree has
+ // been built, but whose right subtree has not yet been built. The
+ // stack is implemented as linked list. The nodes are linked
+ // together by having the "greater" handle of a node set to the
+ // next node in the list. "less_parent" is the handle of the first
+ // node in the list.
+ handle less_parent = null();
+
+ // h is root of current subtree, child is one of its children.
+ handle h, child;
+
+ for (;;) {
+ while (num_sub > 2) {
+ // Subtract one for root of subtree.
+ num_sub--;
+ rem[depth] = !!(num_sub & 1);
+ branch[depth++] = false;
+ num_sub >>= 1;
+ }
+
+ if (num_sub == 2) {
+ // Build a subtree with two nodes, slanting to greater.
+ // I arbitrarily chose to always have the extra node in the
+ // greater subtree when there is an odd number of nodes to
+ // split between the two subtrees.
+
+ h = *p;
+ p++;
+ child = *p;
+ p++;
+ set_lt(child, null());
+ set_gt(child, null());
+ set_bf(child, 0);
+ set_gt(h, child);
+ set_lt(h, null());
+ set_bf(h, 1);
+ } else { // num_sub == 1
+ // Build a subtree with one node.
+
+ h = *p;
+ p++;
+ set_lt(h, null());
+ set_gt(h, null());
+ set_bf(h, 0);
+ }
+
+ while (depth) {
+ depth--;
+ if (!branch[depth]) {
+ // We've completed a less subtree.
+ break;
+ }
+
+ // We've completed a greater subtree, so attach it to
+ // its parent (that is less than it). We pop the parent
+ // off the stack of less parents.
+ child = h;
+ h = less_parent;
+ less_parent = get_gt(h);
+ set_gt(h, child);
+ // num_sub = 2 * (num_sub - rem[depth]) + rem[depth] + 1
+ num_sub <<= 1;
+ num_sub += 1 - rem[depth];
+ if (num_sub & (num_sub - 1)) {
+ // num_sub is not a power of 2
+ set_bf(h, 0);
+ } else {
+ // num_sub is a power of 2
+ set_bf(h, 1);
+ }
+ }
+
+ if (num_sub == num_nodes) {
+ // We've completed the full tree.
+ break;
+ }
+
+ // The subtree we've completed is the less subtree of the
+ // next node in the sequence.
+
+ child = h;
+ h = *p;
+ p++;
+ set_lt(h, child);
+
+ // Put h into stack of less parents.
+ set_gt(h, less_parent);
+ less_parent = h;
+
+ // Proceed to creating greater than subtree of h.
+ branch[depth] = true;
+ num_sub += rem[depth++];
+
+ } // end for (;;)
+
+ abs.root = h;
+
+ return true;
+ }
+
+ protected:
+
+ friend class Iterator;
+
+ // Create a class whose sole purpose is to take advantage of
+ // the "empty member" optimization.
+ struct abs_plus_root : public Abstractor {
+ // The handle of the root element in the AVL tree.
+ handle root;
+ };
+
+ abs_plus_root abs;
+
+ handle get_lt(handle h) { return abs.get_less(h); }
+ void set_lt(handle h, handle lh) { abs.set_less(h, lh); }
+
+ handle get_gt(handle h) { return abs.get_greater(h); }
+ void set_gt(handle h, handle gh) { abs.set_greater(h, gh); }
+
+ int get_bf(handle h) { return abs.get_balance_factor(h); }
+ void set_bf(handle h, int bf) { abs.set_balance_factor(h, bf); }
+
+ int cmp_k_n(key k, handle h) { return abs.compare_key_node(k, h); }
+ int cmp_n_n(handle h1, handle h2) { return abs.compare_node_node(h1, h2); }
+
+ handle null() { return abs.null(); }
+
+ private:
+
+ // Balances subtree, returns handle of root node of subtree
+ // after balancing.
+ handle balance(handle bal_h) {
+ handle deep_h;
+
+ // Either the "greater than" or the "less than" subtree of
+ // this node has to be 2 levels deeper (or else it wouldn't
+ // need balancing).
+
+ if (get_bf(bal_h) > 0) {
+ // "Greater than" subtree is deeper.
+
+ deep_h = get_gt(bal_h);
+
+ if (get_bf(deep_h) < 0) {
+ handle old_h = bal_h;
+ bal_h = get_lt(deep_h);
+
+ set_gt(old_h, get_lt(bal_h));
+ set_lt(deep_h, get_gt(bal_h));
+ set_lt(bal_h, old_h);
+ set_gt(bal_h, deep_h);
+
+ int bf = get_bf(bal_h);
+ if (bf != 0) {
+ if (bf > 0) {
+ set_bf(old_h, -1);
+ set_bf(deep_h, 0);
+ } else {
+ set_bf(deep_h, 1);
+ set_bf(old_h, 0);
+ }
+ set_bf(bal_h, 0);
+ } else {
+ set_bf(old_h, 0);
+ set_bf(deep_h, 0);
+ }
+ } else {
+ set_gt(bal_h, get_lt(deep_h));
+ set_lt(deep_h, bal_h);
+ if (get_bf(deep_h) == 0) {
+ set_bf(deep_h, -1);
+ set_bf(bal_h, 1);
+ } else {
+ set_bf(deep_h, 0);
+ set_bf(bal_h, 0);
+ }
+ bal_h = deep_h;
+ }
+ } else {
+ // "Less than" subtree is deeper.
+
+ deep_h = get_lt(bal_h);
+
+ if (get_bf(deep_h) > 0) {
+ handle old_h = bal_h;
+ bal_h = get_gt(deep_h);
+ set_lt(old_h, get_gt(bal_h));
+ set_gt(deep_h, get_lt(bal_h));
+ set_gt(bal_h, old_h);
+ set_lt(bal_h, deep_h);
+
+ int bf = get_bf(bal_h);
+ if (bf != 0) {
+ if (bf < 0) {
+ set_bf(old_h, 1);
+ set_bf(deep_h, 0);
+ } else {
+ set_bf(deep_h, -1);
+ set_bf(old_h, 0);
+ }
+ set_bf(bal_h, 0);
+ } else {
+ set_bf(old_h, 0);
+ set_bf(deep_h, 0);
+ }
+ } else {
+ set_lt(bal_h, get_gt(deep_h));
+ set_gt(deep_h, bal_h);
+ if (get_bf(deep_h) == 0) {
+ set_bf(deep_h, 1);
+ set_bf(bal_h, -1);
+ } else {
+ set_bf(deep_h, 0);
+ set_bf(bal_h, 0);
+ }
+ bal_h = deep_h;
+ }
+ }
+
+ return bal_h;
+ }
+
+};
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::insert(handle h) {
+ set_lt(h, null());
+ set_gt(h, null());
+ set_bf(h, 0);
+
+ if (abs.root == null()) {
+ abs.root = h;
+ } else {
+ // Last unbalanced node encountered in search for insertion point.
+ handle unbal = null();
+ // Parent of last unbalanced node.
+ handle parent_unbal = null();
+ // Balance factor of last unbalanced node.
+ int unbal_bf;
+
+ // Zero-based depth in tree.
+ unsigned depth = 0, unbal_depth = 0;
+
+ // Records a path into the tree. If branch[n] is true, indicates
+ // take greater branch from the nth node in the path, otherwise
+ // take the less branch. branch[0] gives branch from root, and
+ // so on.
+ BSet branch;
+
+ handle hh = abs.root;
+ handle parent = null();
+ int cmp;
+
+ do {
+ if (get_bf(hh) != 0) {
+ unbal = hh;
+ parent_unbal = parent;
+ unbal_depth = depth;
+ }
+ cmp = cmp_n_n(h, hh);
+ if (cmp == 0) {
+ // Duplicate key.
+ return hh;
+ }
+ parent = hh;
+ hh = cmp < 0 ? get_lt(hh) : get_gt(hh);
+ branch[depth++] = cmp > 0;
+ } while (hh != null());
+
+ // Add node to insert as leaf of tree.
+ if (cmp < 0)
+ set_lt(parent, h);
+ else
+ set_gt(parent, h);
+
+ depth = unbal_depth;
+
+ if (unbal == null()) {
+ hh = abs.root;
+ } else {
+ cmp = branch[depth++] ? 1 : -1;
+ unbal_bf = get_bf(unbal);
+ if (cmp < 0)
+ unbal_bf--;
+ else // cmp > 0
+ unbal_bf++;
+ hh = cmp < 0 ? get_lt(unbal) : get_gt(unbal);
+ if ((unbal_bf != -2) && (unbal_bf != 2)) {
+ // No rebalancing of tree is necessary.
+ set_bf(unbal, unbal_bf);
+ unbal = null();
+ }
+ }
+
+ if (hh != null()) {
+ while (h != hh) {
+ cmp = branch[depth++] ? 1 : -1;
+ if (cmp < 0) {
+ set_bf(hh, -1);
+ hh = get_lt(hh);
+ } else { // cmp > 0
+ set_bf(hh, 1);
+ hh = get_gt(hh);
+ }
+ }
+ }
+
+ if (unbal != null()) {
+ unbal = balance(unbal);
+ if (parent_unbal == null()) {
+ abs.root = unbal;
+ } else {
+ depth = unbal_depth - 1;
+ cmp = branch[depth] ? 1 : -1;
+ if (cmp < 0)
+ set_lt(parent_unbal, unbal);
+ else // cmp > 0
+ set_gt(parent_unbal, unbal);
+ }
+ }
+ }
+
+ return h;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::search(
+ key k,
+ typename AVLTree<Abstractor, maxDepth, BSet>::SearchType st) {
+ const int kMaskHighBit = (int) ~((~(unsigned) 0) >> 1);
+
+ int cmp, target_cmp;
+ handle match_h = null();
+ handle h = abs.root;
+
+ if (st & LESS)
+ target_cmp = 1;
+ else if (st & GREATER)
+ target_cmp = -1;
+ else
+ target_cmp = 0;
+
+ while (h != null()) {
+ cmp = cmp_k_n(k, h);
+ if (cmp == 0) {
+ if (st & EQUAL) {
+ match_h = h;
+ break;
+ }
+ cmp = -target_cmp;
+ } else if (target_cmp != 0) {
+ if (!((cmp ^ target_cmp) & kMaskHighBit)) {
+ // cmp and target_cmp are both positive or both negative.
+ match_h = h;
+ }
+ }
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ }
+
+ return match_h;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::search_least() {
+ handle h = abs.root, parent = null();
+
+ while (h != null()) {
+ parent = h;
+ h = get_lt(h);
+ }
+
+ return parent;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::search_greatest() {
+ handle h = abs.root, parent = null();
+
+ while (h != null()) {
+ parent = h;
+ h = get_gt(h);
+ }
+
+ return parent;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::remove(key k) {
+ // Zero-based depth in tree.
+ unsigned depth = 0, rm_depth;
+
+ // Records a path into the tree. If branch[n] is true, indicates
+ // take greater branch from the nth node in the path, otherwise
+ // take the less branch. branch[0] gives branch from root, and
+ // so on.
+ BSet branch;
+
+ handle h = abs.root;
+ handle parent = null(), child;
+ int cmp, cmp_shortened_sub_with_path = 0;
+
+ for (;;) {
+ if (h == null()) {
+ // No node in tree with given key.
+ return null();
+ }
+ cmp = cmp_k_n(k, h);
+ if (cmp == 0) {
+ // Found node to remove.
+ break;
+ }
+ parent = h;
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ branch[depth++] = cmp > 0;
+ cmp_shortened_sub_with_path = cmp;
+ }
+ handle rm = h;
+ handle parent_rm = parent;
+ rm_depth = depth;
+
+ // If the node to remove is not a leaf node, we need to get a
+ // leaf node, or a node with a single leaf as its child, to put
+ // in the place of the node to remove. We will get the greatest
+ // node in the less subtree (of the node to remove), or the least
+ // node in the greater subtree. We take the leaf node from the
+ // deeper subtree, if there is one.
+
+ if (get_bf(h) < 0) {
+ child = get_lt(h);
+ branch[depth] = false;
+ cmp = -1;
+ } else {
+ child = get_gt(h);
+ branch[depth] = true;
+ cmp = 1;
+ }
+ depth++;
+
+ if (child != null()) {
+ cmp = -cmp;
+ do {
+ parent = h;
+ h = child;
+ if (cmp < 0) {
+ child = get_lt(h);
+ branch[depth] = false;
+ } else {
+ child = get_gt(h);
+ branch[depth] = true;
+ }
+ depth++;
+ } while (child != null());
+
+ if (parent == rm) {
+ // Only went through do loop once. Deleted node will be replaced
+ // in the tree structure by one of its immediate children.
+ cmp_shortened_sub_with_path = -cmp;
+ } else {
+ cmp_shortened_sub_with_path = cmp;
+ }
+
+ // Get the handle of the opposite child, which may not be null.
+ child = cmp > 0 ? get_lt(h) : get_gt(h);
+ }
+
+ if (parent == null()) {
+ // There were only 1 or 2 nodes in this tree.
+ abs.root = child;
+ } else if (cmp_shortened_sub_with_path < 0) {
+ set_lt(parent, child);
+ } else {
+ set_gt(parent, child);
+ }
+
+ // "path" is the parent of the subtree being eliminated or reduced
+ // from a depth of 2 to 1. If "path" is the node to be removed, we
+ // set path to the node we're about to poke into the position of the
+ // node to be removed.
+ handle path = parent == rm ? h : parent;
+
+ if (h != rm) {
+ // Poke in the replacement for the node to be removed.
+ set_lt(h, get_lt(rm));
+ set_gt(h, get_gt(rm));
+ set_bf(h, get_bf(rm));
+ if (parent_rm == null()) {
+ abs.root = h;
+ } else {
+ depth = rm_depth - 1;
+ if (branch[depth])
+ set_gt(parent_rm, h);
+ else
+ set_lt(parent_rm, h);
+ }
+ }
+
+ if (path != null()) {
+ // Create a temporary linked list from the parent of the path node
+ // to the root node.
+ h = abs.root;
+ parent = null();
+ depth = 0;
+ while (h != path) {
+ if (branch[depth++]) {
+ child = get_gt(h);
+ set_gt(h, parent);
+ } else {
+ child = get_lt(h);
+ set_lt(h, parent);
+ }
+ parent = h;
+ h = child;
+ }
+
+ // Climb from the path node to the root node using the linked
+ // list, restoring the tree structure and rebalancing as necessary.
+ bool reduced_depth = true;
+ int bf;
+ cmp = cmp_shortened_sub_with_path;
+ for (;;) {
+ if (reduced_depth) {
+ bf = get_bf(h);
+ if (cmp < 0)
+ bf++;
+ else // cmp > 0
+ bf--;
+ if ((bf == -2) || (bf == 2)) {
+ h = balance(h);
+ bf = get_bf(h);
+ } else {
+ set_bf(h, bf);
+ }
+ reduced_depth = (bf == 0);
+ }
+ if (parent == null())
+ break;
+ child = h;
+ h = parent;
+ cmp = branch[--depth] ? 1 : -1;
+ if (cmp < 0) {
+ parent = get_lt(h);
+ set_lt(h, child);
+ } else {
+ parent = get_gt(h);
+ set_gt(h, child);
+ }
+ }
+ abs.root = h;
+ }
+
+ return rm;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::subst(handle new_node) {
+ handle h = abs.root;
+ handle parent = null();
+ int cmp, last_cmp;
+
+ // Search for node already in tree with same key.
+ for (;;) {
+ if (h == null()) {
+ // No node in tree with same key as new node.
+ return null();
+ }
+ cmp = cmp_n_n(new_node, h);
+ if (cmp == 0) {
+ // Found the node to substitute new one for.
+ break;
+ }
+ last_cmp = cmp;
+ parent = h;
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ }
+
+ // Copy tree housekeeping fields from node in tree to new node.
+ set_lt(new_node, get_lt(h));
+ set_gt(new_node, get_gt(h));
+ set_bf(new_node, get_bf(h));
+
+ if (parent == null()) {
+ // New node is also new root.
+ abs.root = new_node;
+ } else {
+ // Make parent point to new node.
+ if (last_cmp < 0)
+ set_lt(parent, new_node);
+ else
+ set_gt(parent, new_node);
+ }
+
+ return h;
+}
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_AVLTREE_H_
diff --git a/content/browser/indexed_db/leveldb/fixed_array.h b/content/browser/indexed_db/leveldb/fixed_array.h
new file mode 100644
index 0000000..8e67a63
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/fixed_array.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_FIXED_ARRAY_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_FIXED_ARRAY_H_
+
+#include "base/logging.h"
+
+namespace content {
+
+template <typename T, size_t Size> class FixedArray {
+ public:
+ T& operator[](size_t i) {
+#if defined(ADDRESS_SANITIZER)
+ CHECK(i < Size);
+#endif
+ return m_data[i];
+ }
+
+ const T& operator[](size_t i) const {
+#if defined(ADDRESS_SANITIZER)
+ CHECK(i < Size);
+#endif
+ return m_data[i];
+ }
+
+ T* data() { return m_data; }
+ size_t size() const { return Size; }
+
+ private:
+ T m_data[Size];
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_FIXED_ARRAY_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_comparator.h b/content/browser/indexed_db/leveldb/leveldb_comparator.h
new file mode 100644
index 0000000..920d97e
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_comparator.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_COMPARATOR_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_COMPARATOR_H_
+
+#include "base/string16.h"
+
+namespace content {
+
+class LevelDBSlice;
+
+class LevelDBComparator {
+ public:
+ virtual ~LevelDBComparator() {}
+
+ virtual int Compare(const LevelDBSlice& a, const LevelDBSlice& b) const = 0;
+ virtual const char* Name() const = 0;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_COMPARATOR_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.cc b/content/browser/indexed_db/leveldb/leveldb_database.cc
new file mode 100644
index 0000000..b843230
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_database.cc
@@ -0,0 +1,362 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/leveldb/leveldb_database.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/string16.h"
+#include "base/sys_info.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+#include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
+#include "third_party/leveldatabase/env_idb.h"
+#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
+#include "third_party/leveldatabase/src/include/leveldb/comparator.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/env.h"
+#include "third_party/leveldatabase/src/include/leveldb/slice.h"
+
+namespace content {
+
+static leveldb::Slice MakeSlice(const std::vector<char>& value) {
+ DCHECK_GT(value.size(), static_cast<size_t>(0));
+ return leveldb::Slice(&*value.begin(), value.size());
+}
+
+static leveldb::Slice MakeSlice(const LevelDBSlice& s) {
+ return leveldb::Slice(s.begin(), s.end() - s.begin());
+}
+
+static LevelDBSlice MakeLevelDBSlice(const leveldb::Slice& s) {
+ return LevelDBSlice(s.data(), s.data() + s.size());
+}
+
+class ComparatorAdapter : public leveldb::Comparator {
+ public:
+ explicit ComparatorAdapter(const LevelDBComparator* comparator)
+ : comparator_(comparator) {}
+
+ virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const
+ OVERRIDE {
+ return comparator_->Compare(MakeLevelDBSlice(a), MakeLevelDBSlice(b));
+ }
+
+ virtual const char* Name() const OVERRIDE { return comparator_->Name(); }
+
+ // TODO(jsbell): Support the methods below in the future.
+ virtual void FindShortestSeparator(std::string* start,
+ const leveldb::Slice& limit) const
+ OVERRIDE {}
+ virtual void FindShortSuccessor(std::string* key) const OVERRIDE {}
+
+ private:
+ const LevelDBComparator* comparator_;
+};
+
+LevelDBSnapshot::LevelDBSnapshot(LevelDBDatabase* db)
+ : db_(db->db_.get()), snapshot_(db_->GetSnapshot()) {}
+
+LevelDBSnapshot::~LevelDBSnapshot() { db_->ReleaseSnapshot(snapshot_); }
+
+LevelDBDatabase::LevelDBDatabase() {}
+
+LevelDBDatabase::~LevelDBDatabase() {
+ // db_'s destructor uses comparator_adapter_; order of deletion is important.
+ db_.reset();
+ comparator_adapter_.reset();
+ env_.reset();
+}
+
+static leveldb::Status OpenDB(leveldb::Comparator* comparator,
+ leveldb::Env* env,
+ const base::FilePath& path,
+ leveldb::DB** db) {
+ leveldb::Options options;
+ options.comparator = comparator;
+ options.create_if_missing = true;
+ options.paranoid_checks = true;
+
+ // Marking compression as explicitly off so snappy support can be
+ // compiled in for other leveldb clients without implicitly enabling
+ // it for IndexedDB. http://crbug.com/81384
+ options.compression = leveldb::kNoCompression;
+
+ // 20 max_open_files is the minimum LevelDB allows but its cache behaves
+ // poorly with less than 4 files per shard. As of this writing the latest
+ // leveldb (1.9) hardcodes 16 shards. See
+ // https://code.google.com/p/chromium/issues/detail?id=227313#c11
+ options.max_open_files = 80;
+ options.env = env;
+
+ // ChromiumEnv assumes UTF8, converts back to FilePath before using.
+ return leveldb::DB::Open(options, path.AsUTF8Unsafe(), db);
+}
+
+bool LevelDBDatabase::Destroy(const base::FilePath& file_name) {
+ leveldb::Options options;
+ options.env = leveldb::IDBEnv();
+ // ChromiumEnv assumes UTF8, converts back to FilePath before using.
+ const leveldb::Status s =
+ leveldb::DestroyDB(file_name.AsUTF8Unsafe(), options);
+ return s.ok();
+}
+
+static void HistogramFreeSpace(const char* type,
+ const base::FilePath& file_name) {
+ string16 name = ASCIIToUTF16("WebCore.IndexedDB.LevelDB.Open") +
+ ASCIIToUTF16(type) + ASCIIToUTF16("FreeDiskSpace");
+ int64 free_disk_space_in_k_bytes =
+ base::SysInfo::AmountOfFreeDiskSpace(file_name) / 1024;
+ if (free_disk_space_in_k_bytes < 0) {
+ base::Histogram::FactoryGet(
+ "WebCore.IndexedDB.LevelDB.FreeDiskSpaceFailure",
+ 1,
+ 2 /*boundary*/,
+ 2 /*boundary*/ + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)->Add(1 /*sample*/);
+ return;
+ }
+ int clamped_disk_space_k_bytes =
+ free_disk_space_in_k_bytes > INT_MAX ? INT_MAX
+ : free_disk_space_in_k_bytes;
+ const uint64 histogram_max = static_cast<uint64>(1e9);
+ COMPILE_ASSERT(histogram_max <= INT_MAX, histogram_max_too_big);
+ base::Histogram::FactoryGet(UTF16ToUTF8(name),
+ 1,
+ histogram_max,
+ 11 /*buckets*/,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(clamped_disk_space_k_bytes);
+}
+
+static void HistogramLevelDBError(const char* histogram_name,
+ const leveldb::Status& s) {
+ DCHECK(!s.ok());
+ enum {
+ LEVEL_DB_NOT_FOUND,
+ LEVEL_DB_CORRUPTION,
+ LEVEL_DB_IO_ERROR,
+ LEVEL_DB_OTHER,
+ LEVEL_DB_MAX_ERROR
+ };
+ int leveldb_error = LEVEL_DB_OTHER;
+ if (s.IsNotFound())
+ leveldb_error = LEVEL_DB_NOT_FOUND;
+ else if (s.IsCorruption())
+ leveldb_error = LEVEL_DB_CORRUPTION;
+ else if (s.IsIOError())
+ leveldb_error = LEVEL_DB_IO_ERROR;
+ base::Histogram::FactoryGet(histogram_name,
+ 1,
+ LEVEL_DB_MAX_ERROR,
+ LEVEL_DB_MAX_ERROR + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+ ->Add(leveldb_error);
+}
+
+scoped_ptr<LevelDBDatabase> LevelDBDatabase::Open(
+ const base::FilePath& file_name,
+ const LevelDBComparator* comparator) {
+ scoped_ptr<ComparatorAdapter> comparator_adapter(
+ new ComparatorAdapter(comparator));
+
+ leveldb::DB* db;
+ const leveldb::Status s =
+ OpenDB(comparator_adapter.get(), leveldb::IDBEnv(), file_name, &db);
+
+ if (!s.ok()) {
+ HistogramLevelDBError("WebCore.IndexedDB.LevelDBOpenErrors", s);
+ HistogramFreeSpace("Failure", file_name);
+
+ LOG(ERROR) << "Failed to open LevelDB database from "
+ << file_name.AsUTF8Unsafe() << "," << s.ToString();
+ return scoped_ptr<LevelDBDatabase>();
+ }
+
+ HistogramFreeSpace("Success", file_name);
+
+ scoped_ptr<LevelDBDatabase> result(new LevelDBDatabase);
+ result->db_ = make_scoped_ptr(db);
+ result->comparator_adapter_ = comparator_adapter.Pass();
+ result->comparator_ = comparator;
+
+ return result.Pass();
+}
+
+scoped_ptr<LevelDBDatabase> LevelDBDatabase::OpenInMemory(
+ const LevelDBComparator* comparator) {
+ scoped_ptr<ComparatorAdapter> comparator_adapter(
+ new ComparatorAdapter(comparator));
+ scoped_ptr<leveldb::Env> in_memory_env(leveldb::NewMemEnv(leveldb::IDBEnv()));
+
+ leveldb::DB* db;
+ const leveldb::Status s = OpenDB(
+ comparator_adapter.get(), in_memory_env.get(), base::FilePath(), &db);
+
+ if (!s.ok()) {
+ LOG(ERROR) << "Failed to open in-memory LevelDB database: " << s.ToString();
+ return scoped_ptr<LevelDBDatabase>();
+ }
+
+ scoped_ptr<LevelDBDatabase> result(new LevelDBDatabase);
+ result->env_ = in_memory_env.Pass();
+ result->db_ = make_scoped_ptr(db);
+ result->comparator_adapter_ = comparator_adapter.Pass();
+ result->comparator_ = comparator;
+
+ return result.Pass();
+}
+
+bool LevelDBDatabase::Put(const LevelDBSlice& key,
+ const std::vector<char>& value) {
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+
+ const leveldb::Status s =
+ db_->Put(write_options, MakeSlice(key), MakeSlice(value));
+ if (s.ok())
+ return true;
+ LOG(ERROR) << "LevelDB put failed: " << s.ToString();
+ return false;
+}
+
+bool LevelDBDatabase::Remove(const LevelDBSlice& key) {
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+
+ const leveldb::Status s = db_->Delete(write_options, MakeSlice(key));
+ if (s.ok())
+ return true;
+ if (s.IsNotFound())
+ return false;
+ LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
+ return false;
+}
+
+bool LevelDBDatabase::Get(const LevelDBSlice& key,
+ std::vector<char>& value,
+ bool& found,
+ const LevelDBSnapshot* snapshot) {
+ found = false;
+ std::string result;
+ leveldb::ReadOptions read_options;
+ read_options.verify_checksums = true; // TODO(jsbell): Disable this if the
+ // performance impact is too great.
+ read_options.snapshot = snapshot ? snapshot->snapshot_ : 0;
+
+ const leveldb::Status s = db_->Get(read_options, MakeSlice(key), &result);
+ if (s.ok()) {
+ found = true;
+ value.clear();
+ value.insert(value.end(), result.begin(), result.end());
+ return true;
+ }
+ if (s.IsNotFound())
+ return true;
+ LOG(ERROR) << "LevelDB get failed: " << s.ToString();
+ return false;
+}
+
+bool LevelDBDatabase::Write(LevelDBWriteBatch& write_batch) {
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+
+ const leveldb::Status s =
+ db_->Write(write_options, write_batch.write_batch_.get());
+ if (s.ok())
+ return true;
+ HistogramLevelDBError("WebCore.IndexedDB.LevelDBWriteErrors", s);
+ LOG(ERROR) << "LevelDB write failed: " << s.ToString();
+ return false;
+}
+
+namespace {
+class IteratorImpl : public LevelDBIterator {
+ public:
+ virtual ~IteratorImpl() {}
+
+ virtual bool IsValid() const OVERRIDE;
+ virtual void SeekToLast() OVERRIDE;
+ virtual void Seek(const LevelDBSlice& target) OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Prev() OVERRIDE;
+ virtual LevelDBSlice Key() const OVERRIDE;
+ virtual LevelDBSlice Value() const OVERRIDE;
+
+ private:
+ friend class content::LevelDBDatabase;
+ IteratorImpl(scoped_ptr<leveldb::Iterator> iterator);
+ void CheckStatus();
+
+ scoped_ptr<leveldb::Iterator> iterator_;
+};
+}
+
+IteratorImpl::IteratorImpl(scoped_ptr<leveldb::Iterator> it)
+ : iterator_(it.Pass()) {}
+
+void IteratorImpl::CheckStatus() {
+ const leveldb::Status s = iterator_->status();
+ if (!s.ok())
+ LOG(ERROR) << "LevelDB iterator error: " << s.ToString();
+}
+
+bool IteratorImpl::IsValid() const { return iterator_->Valid(); }
+
+void IteratorImpl::SeekToLast() {
+ iterator_->SeekToLast();
+ CheckStatus();
+}
+
+void IteratorImpl::Seek(const LevelDBSlice& target) {
+ iterator_->Seek(MakeSlice(target));
+ CheckStatus();
+}
+
+void IteratorImpl::Next() {
+ DCHECK(IsValid());
+ iterator_->Next();
+ CheckStatus();
+}
+
+void IteratorImpl::Prev() {
+ DCHECK(IsValid());
+ iterator_->Prev();
+ CheckStatus();
+}
+
+LevelDBSlice IteratorImpl::Key() const {
+ DCHECK(IsValid());
+ return MakeLevelDBSlice(iterator_->key());
+}
+
+LevelDBSlice IteratorImpl::Value() const {
+ DCHECK(IsValid());
+ return MakeLevelDBSlice(iterator_->value());
+}
+
+scoped_ptr<LevelDBIterator> LevelDBDatabase::CreateIterator(
+ const LevelDBSnapshot* snapshot) {
+ leveldb::ReadOptions read_options;
+ read_options.verify_checksums = true; // TODO(jsbell): Disable this if the
+ // performance impact is too great.
+ read_options.snapshot = snapshot ? snapshot->snapshot_ : 0;
+ scoped_ptr<leveldb::Iterator> i(db_->NewIterator(read_options));
+ if (!i) // TODO(jsbell): Double check if we actually need to check this.
+ return scoped_ptr<LevelDBIterator>();
+ return scoped_ptr<LevelDBIterator>(new IteratorImpl(i.Pass()));
+}
+
+const LevelDBComparator* LevelDBDatabase::Comparator() const {
+ return comparator_;
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.h b/content/browser/indexed_db/leveldb/leveldb_database.h
new file mode 100644
index 0000000..0c7427b
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_database.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_DATABASE_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_DATABASE_H_
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "content/common/content_export.h"
+
+namespace leveldb {
+class Comparator;
+class DB;
+class Env;
+class Snapshot;
+}
+
+namespace content {
+
+class LevelDBComparator;
+class LevelDBDatabase;
+class LevelDBIterator;
+class LevelDBSlice;
+class LevelDBWriteBatch;
+
+class LevelDBSnapshot {
+ private:
+ friend class LevelDBDatabase;
+ friend class LevelDBTransaction;
+
+ explicit LevelDBSnapshot(LevelDBDatabase* db);
+ ~LevelDBSnapshot();
+
+ leveldb::DB* db_;
+ const leveldb::Snapshot* snapshot_;
+};
+
+class CONTENT_EXPORT LevelDBDatabase {
+ public:
+ static scoped_ptr<LevelDBDatabase> Open(const base::FilePath& file_name,
+ const LevelDBComparator* comparator);
+ static scoped_ptr<LevelDBDatabase> OpenInMemory(
+ const LevelDBComparator* comparator);
+ static bool Destroy(const base::FilePath& file_name);
+ virtual ~LevelDBDatabase();
+
+ bool Put(const LevelDBSlice& key, const std::vector<char>& value);
+ bool Remove(const LevelDBSlice& key);
+ virtual bool Get(const LevelDBSlice& key,
+ std::vector<char>& value,
+ bool& found,
+ const LevelDBSnapshot* = 0);
+ bool Write(LevelDBWriteBatch& batch);
+ scoped_ptr<LevelDBIterator> CreateIterator(const LevelDBSnapshot* = 0);
+ const LevelDBComparator* Comparator() const;
+
+ protected:
+ LevelDBDatabase();
+
+ private:
+ friend class LevelDBSnapshot;
+
+ scoped_ptr<leveldb::Env> env_;
+ scoped_ptr<leveldb::Comparator> comparator_adapter_;
+ scoped_ptr<leveldb::DB> db_;
+ const LevelDBComparator* comparator_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_DATABASE_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_iterator.h b/content/browser/indexed_db/leveldb/leveldb_iterator.h
new file mode 100644
index 0000000..b78747f
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_iterator.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_ITERATOR_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_ITERATOR_H_
+
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+
+namespace content {
+
+class LevelDBIterator {
+ public:
+ virtual ~LevelDBIterator() {}
+ virtual bool IsValid() const = 0;
+ virtual void SeekToLast() = 0;
+ virtual void Seek(const LevelDBSlice& target) = 0;
+ virtual void Next() = 0;
+ virtual void Prev() = 0;
+ virtual LevelDBSlice Key() const = 0;
+ virtual LevelDBSlice Value() const = 0;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_ITERATOR_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_slice.h b/content/browser/indexed_db/leveldb/leveldb_slice.h
new file mode 100644
index 0000000..7962b69
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_slice.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_SLICE_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_SLICE_H_
+
+#include <vector>
+
+#include "base/logging.h"
+
+namespace content {
+
+class LevelDBSlice {
+ public:
+ LevelDBSlice(const char* begin, const char* end) : begin_(begin), end_(end) {
+ DCHECK_GE(end_, begin_);
+ }
+
+ explicit LevelDBSlice(const std::vector<char>& v)
+ : begin_(&*v.begin()), end_(&*v.rbegin() + 1) {
+ DCHECK_GT(v.size(), static_cast<size_t>(0));
+ DCHECK_GE(end_, begin_);
+ }
+
+ ~LevelDBSlice() {}
+
+ const char* begin() const { return begin_; }
+ const char* end() const { return end_; }
+
+ private:
+ const char* begin_;
+ const char* end_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_SLICE_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_transaction.cc b/content/browser/indexed_db/leveldb/leveldb_transaction.cc
new file mode 100644
index 0000000..a8b3a3d
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_transaction.cc
@@ -0,0 +1,490 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
+
+#include "base/logging.h"
+#include "content/browser/indexed_db/leveldb/leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+#include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+
+namespace content {
+
+scoped_refptr<LevelDBTransaction> LevelDBTransaction::Create(
+ LevelDBDatabase* db) {
+ return make_scoped_refptr(new LevelDBTransaction(db));
+}
+
+LevelDBTransaction::LevelDBTransaction(LevelDBDatabase* db)
+ : db_(db), snapshot_(db), comparator_(db->Comparator()), finished_(false) {
+ tree_.abstractor().comparator_ = comparator_;
+}
+
+LevelDBTransaction::AVLTreeNode::AVLTreeNode() {}
+LevelDBTransaction::AVLTreeNode::~AVLTreeNode() {}
+
+void LevelDBTransaction::ClearTree() {
+ TreeType::Iterator iterator;
+ iterator.start_iter_least(tree_);
+
+ std::vector<AVLTreeNode*> nodes;
+
+ while (*iterator) {
+ nodes.push_back(*iterator);
+ ++iterator;
+ }
+ tree_.purge();
+
+ for (size_t i = 0; i < nodes.size(); ++i)
+ delete nodes[i];
+}
+
+LevelDBTransaction::~LevelDBTransaction() { ClearTree(); }
+
+static void InitVector(const LevelDBSlice& slice, std::vector<char>* vector) {
+ vector->clear();
+ vector->insert(vector->end(), slice.begin(), slice.end());
+}
+
+void LevelDBTransaction::Set(const LevelDBSlice& key,
+ const std::vector<char>& value,
+ bool deleted) {
+ DCHECK(!finished_);
+ bool new_node = false;
+ AVLTreeNode* node = tree_.search(key);
+
+ if (!node) {
+ node = new AVLTreeNode;
+ InitVector(key, &node->key);
+ tree_.insert(node);
+ new_node = true;
+ }
+ node->value = value;
+ node->deleted = deleted;
+
+ if (new_node)
+ NotifyIteratorsOfTreeChange();
+}
+
+void LevelDBTransaction::Put(const LevelDBSlice& key,
+ const std::vector<char>& value) {
+ Set(key, value, false);
+}
+
+void LevelDBTransaction::Remove(const LevelDBSlice& key) {
+ Set(key, std::vector<char>(), true);
+}
+
+bool LevelDBTransaction::Get(const LevelDBSlice& key,
+ std::vector<char>& value,
+ bool& found) {
+ found = false;
+ DCHECK(!finished_);
+ AVLTreeNode* node = tree_.search(key);
+
+ if (node) {
+ if (node->deleted)
+ return true;
+
+ value = node->value;
+ found = true;
+ return true;
+ }
+
+ bool ok = db_->Get(key, value, found, &snapshot_);
+ if (!ok) {
+ DCHECK(!found);
+ return false;
+ }
+ return true;
+}
+
+bool LevelDBTransaction::Commit() {
+ DCHECK(!finished_);
+
+ if (tree_.is_empty()) {
+ finished_ = true;
+ return true;
+ }
+
+ scoped_ptr<LevelDBWriteBatch> write_batch = LevelDBWriteBatch::Create();
+
+ TreeType::Iterator iterator;
+ iterator.start_iter_least(tree_);
+
+ while (*iterator) {
+ AVLTreeNode* node = *iterator;
+ if (!node->deleted)
+ write_batch->Put(LevelDBSlice(node->key), LevelDBSlice(node->value));
+ else
+ write_batch->Remove(LevelDBSlice(node->key));
+ ++iterator;
+ }
+
+ if (!db_->Write(*write_batch))
+ return false;
+
+ ClearTree();
+ finished_ = true;
+ return true;
+}
+
+void LevelDBTransaction::Rollback() {
+ DCHECK(!finished_);
+ finished_ = true;
+ ClearTree();
+}
+
+scoped_ptr<LevelDBIterator> LevelDBTransaction::CreateIterator() {
+ return TransactionIterator::Create(this).PassAs<LevelDBIterator>();
+}
+
+scoped_ptr<LevelDBTransaction::TreeIterator>
+LevelDBTransaction::TreeIterator::Create(LevelDBTransaction* transaction) {
+ return make_scoped_ptr(new TreeIterator(transaction));
+}
+
+bool LevelDBTransaction::TreeIterator::IsValid() const { return !!*iterator_; }
+
+void LevelDBTransaction::TreeIterator::SeekToLast() {
+ iterator_.start_iter_greatest(*tree_);
+ if (IsValid())
+ key_ = (*iterator_)->key;
+}
+
+void LevelDBTransaction::TreeIterator::Seek(const LevelDBSlice& target) {
+ iterator_.start_iter(*tree_, target, TreeType::EQUAL);
+ if (!IsValid())
+ iterator_.start_iter(*tree_, target, TreeType::GREATER);
+
+ if (IsValid())
+ key_ = (*iterator_)->key;
+}
+
+void LevelDBTransaction::TreeIterator::Next() {
+ DCHECK(IsValid());
+ ++iterator_;
+ if (IsValid()) {
+ DCHECK(transaction_->comparator_->Compare(LevelDBSlice((*iterator_)->key),
+ LevelDBSlice(key_)) >
+ 0);
+ (void) transaction_;
+ key_ = (*iterator_)->key;
+ }
+}
+
+void LevelDBTransaction::TreeIterator::Prev() {
+ DCHECK(IsValid());
+ --iterator_;
+ if (IsValid()) {
+ DCHECK(tree_->abstractor().comparator_->Compare(
+ LevelDBSlice((*iterator_)->key), LevelDBSlice(key_)) <
+ 0);
+ key_ = (*iterator_)->key;
+ }
+}
+
+LevelDBSlice LevelDBTransaction::TreeIterator::Key() const {
+ DCHECK(IsValid());
+ return LevelDBSlice(key_);
+}
+
+LevelDBSlice LevelDBTransaction::TreeIterator::Value() const {
+ DCHECK(IsValid());
+ DCHECK(!IsDeleted());
+ return LevelDBSlice((*iterator_)->value);
+}
+
+bool LevelDBTransaction::TreeIterator::IsDeleted() const {
+ DCHECK(IsValid());
+ return (*iterator_)->deleted;
+}
+
+void LevelDBTransaction::TreeIterator::Reset() {
+ DCHECK(IsValid());
+ iterator_.start_iter(*tree_, LevelDBSlice(key_), TreeType::EQUAL);
+ DCHECK(IsValid());
+}
+
+LevelDBTransaction::TreeIterator::~TreeIterator() {}
+
+LevelDBTransaction::TreeIterator::TreeIterator(LevelDBTransaction* transaction)
+ : tree_(&transaction->tree_), transaction_(transaction) {}
+
+scoped_ptr<LevelDBTransaction::TransactionIterator>
+LevelDBTransaction::TransactionIterator::Create(
+ scoped_refptr<LevelDBTransaction> transaction) {
+ return make_scoped_ptr(new TransactionIterator(transaction));
+}
+
+LevelDBTransaction::TransactionIterator::TransactionIterator(
+ scoped_refptr<LevelDBTransaction> transaction)
+ : transaction_(transaction),
+ comparator_(transaction_->comparator_),
+ tree_iterator_(TreeIterator::Create(transaction_.get())),
+ db_iterator_(transaction_->db_->CreateIterator(&transaction_->snapshot_)),
+ current_(0),
+ direction_(FORWARD),
+ tree_changed_(false) {
+ transaction_->RegisterIterator(this);
+}
+
+LevelDBTransaction::TransactionIterator::~TransactionIterator() {
+ transaction_->UnregisterIterator(this);
+}
+
+bool LevelDBTransaction::TransactionIterator::IsValid() const {
+ return !!current_;
+}
+
+void LevelDBTransaction::TransactionIterator::SeekToLast() {
+ tree_iterator_->SeekToLast();
+ db_iterator_->SeekToLast();
+ direction_ = REVERSE;
+
+ HandleConflictsAndDeletes();
+ SetCurrentIteratorToLargestKey();
+}
+
+void LevelDBTransaction::TransactionIterator::Seek(const LevelDBSlice& target) {
+ tree_iterator_->Seek(target);
+ db_iterator_->Seek(target);
+ direction_ = FORWARD;
+
+ HandleConflictsAndDeletes();
+ SetCurrentIteratorToSmallestKey();
+}
+
+void LevelDBTransaction::TransactionIterator::Next() {
+ DCHECK(IsValid());
+ if (tree_changed_)
+ RefreshTreeIterator();
+
+ if (direction_ != FORWARD) {
+ // Ensure the non-current iterator is positioned after Key().
+
+ LevelDBIterator* non_current =
+ (current_ == db_iterator_.get()) ? tree_iterator_.get()
+ : db_iterator_.get();
+
+ non_current->Seek(Key());
+ if (non_current->IsValid() &&
+ !comparator_->Compare(non_current->Key(), Key()))
+ non_current->Next(); // Take an extra step so the non-current key is
+ // strictly greater than Key().
+
+ DCHECK(!non_current->IsValid() ||
+ comparator_->Compare(non_current->Key(), Key()) > 0);
+
+ direction_ = FORWARD;
+ }
+
+ current_->Next();
+ HandleConflictsAndDeletes();
+ SetCurrentIteratorToSmallestKey();
+}
+
+void LevelDBTransaction::TransactionIterator::Prev() {
+ DCHECK(IsValid());
+ if (tree_changed_)
+ RefreshTreeIterator();
+
+ if (direction_ != REVERSE) {
+ // Ensure the non-current iterator is positioned before Key().
+
+ LevelDBIterator* non_current =
+ (current_ == db_iterator_.get()) ? tree_iterator_.get()
+ : db_iterator_.get();
+
+ non_current->Seek(Key());
+ if (non_current->IsValid()) {
+ // Iterator is at first entry >= Key().
+ // Step back once to entry < key.
+ // This is why we don't check for the keys being the same before
+ // stepping, like we do in Next() above.
+ non_current->Prev();
+ } else {
+ non_current->SeekToLast(); // Iterator has no entries >= Key(). Position
+ // at last entry.
+ }
+ DCHECK(!non_current->IsValid() ||
+ comparator_->Compare(non_current->Key(), Key()) < 0);
+
+ direction_ = REVERSE;
+ }
+
+ current_->Prev();
+ HandleConflictsAndDeletes();
+ SetCurrentIteratorToLargestKey();
+}
+
+LevelDBSlice LevelDBTransaction::TransactionIterator::Key() const {
+ DCHECK(IsValid());
+ if (tree_changed_)
+ RefreshTreeIterator();
+ return current_->Key();
+}
+
+LevelDBSlice LevelDBTransaction::TransactionIterator::Value() const {
+ DCHECK(IsValid());
+ if (tree_changed_)
+ RefreshTreeIterator();
+ return current_->Value();
+}
+
+void LevelDBTransaction::TransactionIterator::TreeChanged() {
+ tree_changed_ = true;
+}
+
+void LevelDBTransaction::TransactionIterator::RefreshTreeIterator() const {
+ DCHECK(tree_changed_);
+
+ tree_changed_ = false;
+
+ if (tree_iterator_->IsValid() && tree_iterator_.get() == current_) {
+ tree_iterator_->Reset();
+ return;
+ }
+
+ if (db_iterator_->IsValid()) {
+
+ // There could be new nodes in the tree that we should iterate over.
+
+ if (direction_ == FORWARD) {
+ // Try to seek tree iterator to something greater than the db iterator.
+ tree_iterator_->Seek(db_iterator_->Key());
+ if (tree_iterator_->IsValid() &&
+ !comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key()))
+ tree_iterator_->Next(); // If equal, take another step so the tree
+ // iterator is strictly greater.
+ } else {
+ // If going backward, seek to a key less than the db iterator.
+ DCHECK_EQ(REVERSE, direction_);
+ tree_iterator_->Seek(db_iterator_->Key());
+ if (tree_iterator_->IsValid())
+ tree_iterator_->Prev();
+ }
+ }
+}
+
+bool LevelDBTransaction::TransactionIterator::TreeIteratorIsLower() const {
+ return comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key()) < 0;
+}
+
+bool LevelDBTransaction::TransactionIterator::TreeIteratorIsHigher() const {
+ return comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key()) > 0;
+}
+
+void LevelDBTransaction::TransactionIterator::HandleConflictsAndDeletes() {
+ bool loop = true;
+
+ while (loop) {
+ loop = false;
+
+ if (tree_iterator_->IsValid() && db_iterator_->IsValid() &&
+ !comparator_->Compare(tree_iterator_->Key(), db_iterator_->Key())) {
+ // For equal keys, the tree iterator takes precedence, so move the
+ // database iterator another step.
+ if (direction_ == FORWARD)
+ db_iterator_->Next();
+ else
+ db_iterator_->Prev();
+ }
+
+ // Skip over delete markers in the tree iterator until it catches up with
+ // the db iterator.
+ if (tree_iterator_->IsValid() && tree_iterator_->IsDeleted()) {
+ if (direction_ == FORWARD &&
+ (!db_iterator_->IsValid() || TreeIteratorIsLower())) {
+ tree_iterator_->Next();
+ loop = true;
+ } else if (direction_ == REVERSE &&
+ (!db_iterator_->IsValid() || TreeIteratorIsHigher())) {
+ tree_iterator_->Prev();
+ loop = true;
+ }
+ }
+ }
+}
+
+void
+LevelDBTransaction::TransactionIterator::SetCurrentIteratorToSmallestKey() {
+ LevelDBIterator* smallest = 0;
+
+ if (tree_iterator_->IsValid())
+ smallest = tree_iterator_.get();
+
+ if (db_iterator_->IsValid()) {
+ if (!smallest ||
+ comparator_->Compare(db_iterator_->Key(), smallest->Key()) < 0)
+ smallest = db_iterator_.get();
+ }
+
+ current_ = smallest;
+}
+
+void LevelDBTransaction::TransactionIterator::SetCurrentIteratorToLargestKey() {
+ LevelDBIterator* largest = 0;
+
+ if (tree_iterator_->IsValid())
+ largest = tree_iterator_.get();
+
+ if (db_iterator_->IsValid()) {
+ if (!largest ||
+ comparator_->Compare(db_iterator_->Key(), largest->Key()) > 0)
+ largest = db_iterator_.get();
+ }
+
+ current_ = largest;
+}
+
+void LevelDBTransaction::RegisterIterator(TransactionIterator* iterator) {
+ DCHECK(iterators_.find(iterator) == iterators_.end());
+ iterators_.insert(iterator);
+}
+
+void LevelDBTransaction::UnregisterIterator(TransactionIterator* iterator) {
+ DCHECK(iterators_.find(iterator) != iterators_.end());
+ iterators_.erase(iterator);
+}
+
+void LevelDBTransaction::NotifyIteratorsOfTreeChange() {
+ for (std::set<TransactionIterator*>::iterator i = iterators_.begin();
+ i != iterators_.end();
+ ++i) {
+ TransactionIterator* transaction_iterator = *i;
+ transaction_iterator->TreeChanged();
+ }
+}
+
+scoped_ptr<LevelDBWriteOnlyTransaction> LevelDBWriteOnlyTransaction::Create(
+ LevelDBDatabase* db) {
+ return make_scoped_ptr(new LevelDBWriteOnlyTransaction(db));
+}
+
+LevelDBWriteOnlyTransaction::LevelDBWriteOnlyTransaction(LevelDBDatabase* db)
+ : db_(db), write_batch_(LevelDBWriteBatch::Create()), finished_(false) {}
+
+LevelDBWriteOnlyTransaction::~LevelDBWriteOnlyTransaction() {
+ write_batch_->Clear();
+}
+
+void LevelDBWriteOnlyTransaction::Remove(const LevelDBSlice& key) {
+ DCHECK(!finished_);
+ write_batch_->Remove(key);
+}
+
+bool LevelDBWriteOnlyTransaction::Commit() {
+ DCHECK(!finished_);
+
+ if (!db_->Write(*write_batch_))
+ return false;
+
+ finished_ = true;
+ write_batch_->Clear();
+ return true;
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/leveldb/leveldb_transaction.h b/content/browser/indexed_db/leveldb/leveldb_transaction.h
new file mode 100644
index 0000000..39d1a30
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_transaction.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_
+
+#include <set>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/indexed_db/leveldb/avltree.h"
+#include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+
+namespace content {
+
+class LevelDBWriteBatch;
+
+class CONTENT_EXPORT LevelDBTransaction
+ : public base::RefCounted<LevelDBTransaction> {
+ public:
+ static scoped_refptr<LevelDBTransaction> Create(LevelDBDatabase* db);
+
+ void Put(const LevelDBSlice& key, const std::vector<char>& value);
+ void Remove(const LevelDBSlice& key);
+ bool Get(const LevelDBSlice& key, std::vector<char>& value, bool& found);
+ bool Commit();
+ void Rollback();
+
+ scoped_ptr<LevelDBIterator> CreateIterator();
+
+ private:
+ LevelDBTransaction(LevelDBDatabase* db);
+ virtual ~LevelDBTransaction();
+ friend class base::RefCounted<LevelDBTransaction>;
+
+ struct AVLTreeNode {
+ AVLTreeNode();
+ ~AVLTreeNode();
+ std::vector<char> key;
+ std::vector<char> value;
+ bool deleted;
+
+ AVLTreeNode* less;
+ AVLTreeNode* greater;
+ int balance_factor;
+ DISALLOW_COPY_AND_ASSIGN(AVLTreeNode);
+ };
+
+ struct AVLTreeAbstractor {
+ typedef AVLTreeNode* handle;
+ typedef size_t size;
+ typedef LevelDBSlice key;
+
+ handle get_less(handle h) { return h->less; }
+ void set_less(handle h, handle less) { h->less = less; }
+ handle get_greater(handle h) { return h->greater; }
+ void set_greater(handle h, handle greater) { h->greater = greater; }
+
+ int get_balance_factor(handle h) { return h->balance_factor; }
+ void set_balance_factor(handle h, int bf) { h->balance_factor = bf; }
+
+ int compare_key_key(const key& ka, const key& kb) {
+ return comparator_->Compare(ka, kb);
+ }
+ int compare_key_node(const key& k, handle h) {
+ return compare_key_key(k, key(h->key));
+ }
+ int compare_node_node(handle ha, handle hb) {
+ return compare_key_key(key(ha->key), key(hb->key));
+ }
+
+ static handle null() { return 0; }
+
+ const LevelDBComparator* comparator_;
+ };
+
+ typedef AVLTree<AVLTreeAbstractor> TreeType;
+
+ class TreeIterator : public LevelDBIterator {
+ public:
+ static scoped_ptr<TreeIterator> Create(LevelDBTransaction* transaction);
+ virtual ~TreeIterator();
+
+ virtual bool IsValid() const OVERRIDE;
+ virtual void SeekToLast() OVERRIDE;
+ virtual void Seek(const LevelDBSlice& slice) OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Prev() OVERRIDE;
+ virtual LevelDBSlice Key() const OVERRIDE;
+ virtual LevelDBSlice Value() const OVERRIDE;
+ bool IsDeleted() const;
+ void Reset();
+
+ private:
+ TreeIterator(LevelDBTransaction* transaction);
+ mutable TreeType::Iterator iterator_; // Dereferencing this is non-const.
+ TreeType* tree_;
+ LevelDBTransaction* transaction_;
+ std::vector<char> key_;
+ };
+
+ class TransactionIterator : public LevelDBIterator {
+ public:
+ virtual ~TransactionIterator();
+ static scoped_ptr<TransactionIterator> Create(
+ scoped_refptr<LevelDBTransaction> transaction);
+
+ virtual bool IsValid() const OVERRIDE;
+ virtual void SeekToLast() OVERRIDE;
+ virtual void Seek(const LevelDBSlice& target) OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Prev() OVERRIDE;
+ virtual LevelDBSlice Key() const OVERRIDE;
+ virtual LevelDBSlice Value() const OVERRIDE;
+ void TreeChanged();
+
+ private:
+ TransactionIterator(scoped_refptr<LevelDBTransaction> transaction);
+ void HandleConflictsAndDeletes();
+ void SetCurrentIteratorToSmallestKey();
+ void SetCurrentIteratorToLargestKey();
+ void RefreshTreeIterator() const;
+ bool TreeIteratorIsLower() const;
+ bool TreeIteratorIsHigher() const;
+
+ scoped_refptr<LevelDBTransaction> transaction_;
+ const LevelDBComparator* comparator_;
+ mutable scoped_ptr<TreeIterator> tree_iterator_;
+ scoped_ptr<LevelDBIterator> db_iterator_;
+ LevelDBIterator* current_;
+
+ enum Direction {
+ FORWARD,
+ REVERSE
+ };
+ Direction direction_;
+ mutable bool tree_changed_;
+ };
+
+ void Set(const LevelDBSlice& key,
+ const std::vector<char>& value,
+ bool deleted);
+ void ClearTree();
+ void RegisterIterator(TransactionIterator* iterator);
+ void UnregisterIterator(TransactionIterator* iterator);
+ void NotifyIteratorsOfTreeChange();
+
+ LevelDBDatabase* db_;
+ const LevelDBSnapshot snapshot_;
+ const LevelDBComparator* comparator_;
+ TreeType tree_;
+ bool finished_;
+ std::set<TransactionIterator*> iterators_;
+};
+
+class LevelDBWriteOnlyTransaction {
+ public:
+ static scoped_ptr<LevelDBWriteOnlyTransaction> Create(LevelDBDatabase* db);
+
+ ~LevelDBWriteOnlyTransaction();
+ void Remove(const LevelDBSlice& key);
+ bool Commit();
+
+ private:
+ LevelDBWriteOnlyTransaction(LevelDBDatabase* db);
+
+ LevelDBDatabase* db_;
+ scoped_ptr<LevelDBWriteBatch> write_batch_;
+ bool finished_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_unittest.cc b/content/browser/indexed_db/leveldb/leveldb_unittest.cc
new file mode 100644
index 0000000..6508e09
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/platform_file.h"
+#include "base/string16.h"
+#include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+class SimpleComparator : public LevelDBComparator {
+ public:
+ virtual int Compare(const LevelDBSlice& a, const LevelDBSlice& b) const
+ OVERRIDE {
+ size_t len = std::min(a.end() - a.begin(), b.end() - b.begin());
+ return memcmp(a.begin(), b.begin(), len);
+ }
+ virtual const char* Name() const OVERRIDE { return "temp_comparator"; }
+};
+
+std::vector<char> EncodeString(const std::string& s) {
+ std::vector<char> ret(s.size());
+ for (size_t i = 0; i < s.size(); ++i)
+ ret[i] = s[i];
+ return ret;
+}
+
+TEST(LevelDBDatabaseTest, CorruptionTest) {
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+
+ const std::vector<char> key = EncodeString("key");
+ const std::vector<char> put_value = EncodeString("value");
+ std::vector<char> got_value;
+ SimpleComparator comparator;
+
+ scoped_ptr<LevelDBDatabase> leveldb =
+ LevelDBDatabase::Open(temp_directory.path(), &comparator);
+ EXPECT_TRUE(leveldb);
+ bool success = leveldb->Put(LevelDBSlice(key), put_value);
+ EXPECT_TRUE(success);
+ leveldb.Pass();
+ EXPECT_FALSE(leveldb);
+
+ leveldb = LevelDBDatabase::Open(temp_directory.path(), &comparator);
+ EXPECT_TRUE(leveldb);
+ bool found = false;
+ success = leveldb->Get(LevelDBSlice(key), got_value, found);
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(put_value, got_value);
+ leveldb.Pass();
+ EXPECT_FALSE(leveldb);
+
+ base::FilePath file_path = temp_directory.path().AppendASCII("CURRENT");
+ base::PlatformFile handle = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
+ NULL,
+ NULL);
+ base::TruncatePlatformFile(handle, 0);
+ base::ClosePlatformFile(handle);
+
+ leveldb = LevelDBDatabase::Open(temp_directory.path(), &comparator);
+ EXPECT_FALSE(leveldb);
+
+ bool destroyed = LevelDBDatabase::Destroy(temp_directory.path());
+ EXPECT_TRUE(destroyed);
+
+ leveldb = LevelDBDatabase::Open(temp_directory.path(), &comparator);
+ EXPECT_TRUE(leveldb);
+ success = leveldb->Get(LevelDBSlice(key), got_value, found);
+ EXPECT_TRUE(success);
+ EXPECT_FALSE(found);
+}
+
+TEST(LevelDBDatabaseTest, Transaction) {
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+
+ const std::vector<char> key = EncodeString("key");
+ std::vector<char> got_value;
+ SimpleComparator comparator;
+
+ scoped_ptr<LevelDBDatabase> leveldb =
+ LevelDBDatabase::Open(temp_directory.path(), &comparator);
+ EXPECT_TRUE(leveldb);
+
+ const std::vector<char> old_value = EncodeString("value");
+ bool success = leveldb->Put(LevelDBSlice(key), old_value);
+ EXPECT_TRUE(success);
+
+ scoped_refptr<LevelDBTransaction> transaction =
+ LevelDBTransaction::Create(leveldb.get());
+
+ const std::vector<char> new_value = EncodeString("new value");
+ success = leveldb->Put(LevelDBSlice(key), new_value);
+ EXPECT_TRUE(success);
+
+ bool found = false;
+ success = transaction->Get(LevelDBSlice(key), got_value, found);
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(
+ comparator.Compare(LevelDBSlice(got_value), LevelDBSlice(old_value)), 0);
+
+ found = false;
+ success = leveldb->Get(LevelDBSlice(key), got_value, found);
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(
+ comparator.Compare(LevelDBSlice(got_value), LevelDBSlice(new_value)), 0);
+
+ const std::vector<char> added_key = EncodeString("added key");
+ const std::vector<char> added_value = EncodeString("added value");
+ success = leveldb->Put(LevelDBSlice(added_key), added_value);
+ EXPECT_TRUE(success);
+
+ success = leveldb->Get(LevelDBSlice(added_key), got_value, found);
+ EXPECT_TRUE(success);
+ EXPECT_TRUE(found);
+ EXPECT_EQ(
+ comparator.Compare(LevelDBSlice(got_value), LevelDBSlice(added_value)),
+ 0);
+
+ success = transaction->Get(LevelDBSlice(added_key), got_value, found);
+ EXPECT_TRUE(success);
+ EXPECT_FALSE(found);
+}
+
+TEST(LevelDBDatabaseTest, TransactionIterator) {
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+
+ const std::vector<char> key1 = EncodeString("key1");
+ const std::vector<char> value1 = EncodeString("value1");
+ const std::vector<char> key2 = EncodeString("key2");
+ const std::vector<char> value2 = EncodeString("value2");
+
+ SimpleComparator comparator;
+ bool success;
+
+ scoped_ptr<LevelDBDatabase> leveldb =
+ LevelDBDatabase::Open(temp_directory.path(), &comparator);
+ EXPECT_TRUE(leveldb);
+
+ success = leveldb->Put(LevelDBSlice(key1), value1);
+ EXPECT_TRUE(success);
+ success = leveldb->Put(LevelDBSlice(key2), value2);
+ EXPECT_TRUE(success);
+
+ scoped_refptr<LevelDBTransaction> transaction =
+ LevelDBTransaction::Create(leveldb.get());
+
+ success = leveldb->Remove(LevelDBSlice(key2));
+ EXPECT_TRUE(success);
+
+ scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
+
+ const char empty[] = { 0 };
+ it->Seek(LevelDBSlice(empty, empty));
+
+ EXPECT_TRUE(it->IsValid());
+ EXPECT_EQ(comparator.Compare(LevelDBSlice(it->Key()), LevelDBSlice(key1)), 0);
+ EXPECT_EQ(comparator.Compare(it->Value(), LevelDBSlice(value1)), 0);
+
+ it->Next();
+
+ EXPECT_TRUE(it->IsValid());
+ EXPECT_EQ(comparator.Compare(it->Key(), LevelDBSlice(key2)), 0);
+ EXPECT_EQ(comparator.Compare(it->Value(), LevelDBSlice(value2)), 0);
+
+ it->Next();
+
+ EXPECT_FALSE(it->IsValid());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/content/browser/indexed_db/leveldb/leveldb_write_batch.cc b/content/browser/indexed_db/leveldb/leveldb_write_batch.cc
new file mode 100644
index 0000000..b15c4fa
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_write_batch.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
+
+#include "content/browser/indexed_db/leveldb/leveldb_slice.h"
+#include "third_party/leveldatabase/src/include/leveldb/slice.h"
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
+
+namespace content {
+
+scoped_ptr<LevelDBWriteBatch> LevelDBWriteBatch::Create() {
+ return make_scoped_ptr(new LevelDBWriteBatch);
+}
+
+LevelDBWriteBatch::LevelDBWriteBatch()
+ : write_batch_(new leveldb::WriteBatch) {}
+
+LevelDBWriteBatch::~LevelDBWriteBatch() {}
+
+static leveldb::Slice MakeSlice(const LevelDBSlice& s) {
+ return leveldb::Slice(s.begin(), s.end() - s.begin());
+}
+
+void LevelDBWriteBatch::Put(const LevelDBSlice& key,
+ const LevelDBSlice& value) {
+ write_batch_->Put(MakeSlice(key), MakeSlice(value));
+}
+
+void LevelDBWriteBatch::Remove(const LevelDBSlice& key) {
+ write_batch_->Delete(MakeSlice(key));
+}
+
+void LevelDBWriteBatch::Clear() { write_batch_->Clear(); }
+
+} // namespace content
diff --git a/content/browser/indexed_db/leveldb/leveldb_write_batch.h b/content/browser/indexed_db/leveldb/leveldb_write_batch.h
new file mode 100644
index 0000000..2849687
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_write_batch.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_WRITE_BATCH_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_WRITE_BATCH_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace leveldb {
+class WriteBatch;
+}
+
+namespace content {
+
+class LevelDBSlice;
+
+// Wrapper around leveldb::WriteBatch.
+// This class holds a collection of updates to apply atomically to a database.
+class LevelDBWriteBatch {
+ public:
+ static scoped_ptr<LevelDBWriteBatch> Create();
+ ~LevelDBWriteBatch();
+
+ void Put(const LevelDBSlice& key, const LevelDBSlice& value);
+ void Remove(const LevelDBSlice& key); // Add remove operation to the batch.
+ void Clear();
+
+ private:
+ friend class LevelDBDatabase;
+ LevelDBWriteBatch();
+
+ scoped_ptr<leveldb::WriteBatch> write_batch_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_WRITE_BATCH_H_
diff --git a/content/browser/indexed_db/list_set.h b/content/browser/indexed_db/list_set.h
new file mode 100644
index 0000000..2ddd65d
--- /dev/null
+++ b/content/browser/indexed_db/list_set.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LIST_SET_H_
+#define CONTENT_BROWSER_INDEXED_DB_LIST_SET_H_
+
+#include <algorithm>
+#include <iterator>
+#include <list>
+#include <set>
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+
+//
+// A container class that provides fast containment test (like a set)
+// but maintains insertion order for iteration (like list).
+//
+// Member types of value (primitives and objects by value), raw pointers
+// and scoped_refptr<> are supported.
+//
+template <typename T> class list_set {
+ public:
+ list_set() {}
+ list_set(const list_set<T>& other) : list_(other.list_), set_(other.set_) {}
+ list_set& operator=(const list_set<T>& other) {
+ list_ = other.list_;
+ set_ = other.set_;
+ return *this;
+ }
+
+ void insert(const T& elem) {
+ if (set_.find(elem) != set_.end())
+ return;
+ set_.insert(elem);
+ list_.push_back(elem);
+ }
+
+ void erase(const T& elem) {
+ if (set_.find(elem) == set_.end())
+ return;
+ set_.erase(elem);
+ typename std::list<T>::iterator it =
+ std::find(list_.begin(), list_.end(), elem);
+ DCHECK(it != list_.end());
+ list_.erase(it);
+ }
+
+ bool has(const T& elem) { return set_.find(elem) != set_.end(); }
+
+ size_t size() const {
+ DCHECK_EQ(list_.size(), set_.size());
+ return set_.size();
+ }
+
+ bool empty() const {
+ DCHECK_EQ(list_.empty(), set_.empty());
+ return set_.empty();
+ }
+
+ class const_iterator;
+
+ class iterator {
+ public:
+ typedef iterator self_type;
+ typedef T value_type;
+ typedef T& reference;
+ typedef T* pointer;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+
+ inline iterator(typename std::list<T>::iterator it) : it_(it) {}
+ inline self_type& operator++() {
+ ++it_;
+ return *this;
+ }
+ inline self_type operator++(int) {
+ self_type result(*this);
+ ++(*this);
+ return result;
+ }
+ inline self_type& operator--() {
+ --it_;
+ return *this;
+ }
+ inline self_type operator--(int) {
+ self_type result(*this);
+ --(*this);
+ return result;
+ }
+ inline value_type& operator*() { return *it_; }
+ inline value_type* operator->() { return &(*it_); }
+ inline bool operator==(const iterator& rhs) const { return it_ == rhs.it_; }
+ inline bool operator!=(const iterator& rhs) const { return it_ != rhs.it_; }
+
+ inline operator const_iterator() const { return const_iterator(it_); }
+
+ private:
+ typename std::list<T>::iterator it_;
+ };
+
+ class const_iterator {
+ public:
+ typedef const_iterator self_type;
+ typedef T value_type;
+ typedef T& reference;
+ typedef T* pointer;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+
+ inline const_iterator(typename std::list<T>::const_iterator it) : it_(it) {}
+ inline self_type& operator++() {
+ ++it_;
+ return *this;
+ }
+ inline self_type operator++(int) {
+ self_type result(*this);
+ ++(*this);
+ return result;
+ }
+ inline self_type& operator--() {
+ --it_;
+ return *this;
+ }
+ inline self_type operator--(int) {
+ self_type result(*this);
+ --(*this);
+ return result;
+ }
+ inline const value_type& operator*() { return *it_; }
+ inline const value_type* operator->() { return &(*it_); }
+ inline bool operator==(const const_iterator& rhs) const {
+ return it_ == rhs.it_;
+ }
+ inline bool operator!=(const const_iterator& rhs) const {
+ return it_ != rhs.it_;
+ }
+
+ private:
+ typename std::list<T>::const_iterator it_;
+ };
+
+ iterator begin() { return iterator(list_.begin()); }
+ iterator end() { return iterator(list_.end()); }
+ const_iterator begin() const { return const_iterator(list_.begin()); }
+ const_iterator end() const { return const_iterator(list_.end()); }
+
+ private:
+ std::list<T> list_;
+ std::set<T> set_;
+};
+
+// Prevent instantiation of list_set<scoped_ptr<T>> as the current
+// implementation would fail.
+// TODO(jsbell): Support scoped_ptr through specialization.
+template <typename T> class list_set<scoped_ptr<T> >;
+
+#endif // CONTENT_BROWSER_INDEXED_DB_LIST_SET_H_
diff --git a/content/browser/indexed_db/list_set_unittest.cc b/content/browser/indexed_db/list_set_unittest.cc
new file mode 100644
index 0000000..af5d8cf
--- /dev/null
+++ b/content/browser/indexed_db/list_set_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/indexed_db/list_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+TEST(ListSetTest, ListSetIterator) {
+ list_set<int> set;
+ for (int i = 3; i > 0; --i)
+ set.insert(i);
+
+ list_set<int>::iterator it = set.begin();
+ EXPECT_EQ(3, *it);
+ ++it;
+ EXPECT_EQ(2, *it);
+ it++;
+ EXPECT_EQ(1, *it);
+ --it;
+ EXPECT_EQ(2, *it);
+ it--;
+ EXPECT_EQ(3, *it);
+ ++it;
+ EXPECT_EQ(2, *it);
+ it++;
+ EXPECT_EQ(1, *it);
+ ++it;
+ EXPECT_EQ(set.end(), it);
+}
+
+TEST(ListSetTest, ListSetConstIterator) {
+ list_set<int> set;
+ for (int i = 5; i > 0; --i)
+ set.insert(i);
+
+ const list_set<int>& ref = set;
+
+ list_set<int>::const_iterator it = ref.begin();
+ for (int i = 5; i > 0; --i) {
+ EXPECT_EQ(i, *it);
+ ++it;
+ }
+ EXPECT_EQ(ref.end(), it);
+}
+
+TEST(ListSetTest, ListSetPrimitive) {
+ list_set<int> set;
+ EXPECT_TRUE(set.empty());
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ {
+ list_set<int>::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+
+ for (int i = 5; i > 0; --i)
+ set.insert(i);
+ EXPECT_EQ(static_cast<size_t>(5), set.size());
+ EXPECT_FALSE(set.empty());
+
+ set.erase(3);
+ EXPECT_EQ(static_cast<size_t>(4), set.size());
+
+ EXPECT_TRUE(set.has(2));
+ set.erase(2);
+ EXPECT_FALSE(set.has(2));
+ EXPECT_EQ(static_cast<size_t>(3), set.size());
+
+ {
+ list_set<int>::iterator it = set.begin();
+ EXPECT_EQ(5, *it);
+ ++it;
+ EXPECT_EQ(4, *it);
+ ++it;
+ EXPECT_EQ(1, *it);
+ ++it;
+ EXPECT_EQ(set.end(), it);
+ }
+
+ set.erase(1);
+ set.erase(4);
+ set.erase(5);
+
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ EXPECT_TRUE(set.empty());
+ {
+ list_set<int>::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+}
+
+template <typename T> class Wrapped {
+ public:
+ Wrapped(const T& value) : value_(value) {}
+ Wrapped(const Wrapped<T>& other) : value_(other.value_) {}
+ Wrapped& operator=(const Wrapped<T>& rhs) {
+ value_ = rhs.value_;
+ return *this;
+ }
+ int value() const { return value_; }
+ bool operator<(const Wrapped<T>& rhs) const { return value_ < rhs.value_; }
+ bool operator==(const Wrapped<T>& rhs) const { return value_ == rhs.value_; }
+
+ private:
+ T value_;
+};
+
+TEST(ListSetTest, ListSetObject) {
+ list_set<Wrapped<int> > set;
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ {
+ list_set<Wrapped<int> >::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+
+ set.insert(Wrapped<int>(0));
+ set.insert(Wrapped<int>(1));
+ set.insert(Wrapped<int>(2));
+
+ EXPECT_EQ(static_cast<size_t>(3), set.size());
+
+ {
+ list_set<Wrapped<int> >::iterator it = set.begin();
+ EXPECT_EQ(0, it->value());
+ ++it;
+ EXPECT_EQ(1, it->value());
+ ++it;
+ EXPECT_EQ(2, it->value());
+ ++it;
+ EXPECT_EQ(set.end(), it);
+ }
+
+ set.erase(Wrapped<int>(0));
+ set.erase(Wrapped<int>(1));
+ set.erase(Wrapped<int>(2));
+
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ {
+ list_set<Wrapped<int> >::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+}
+
+TEST(ListSetTest, ListSetPointer) {
+ scoped_ptr<Wrapped<int> > w0(new Wrapped<int>(0));
+ scoped_ptr<Wrapped<int> > w1(new Wrapped<int>(1));
+ scoped_ptr<Wrapped<int> > w2(new Wrapped<int>(2));
+
+ list_set<Wrapped<int>*> set;
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ {
+ list_set<Wrapped<int>*>::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+
+ set.insert(w0.get());
+ set.insert(w1.get());
+ set.insert(w2.get());
+
+ EXPECT_EQ(static_cast<size_t>(3), set.size());
+
+ {
+ list_set<Wrapped<int>*>::iterator it = set.begin();
+ EXPECT_EQ(0, (*it)->value());
+ ++it;
+ EXPECT_EQ(1, (*it)->value());
+ ++it;
+ EXPECT_EQ(2, (*it)->value());
+ ++it;
+ EXPECT_EQ(set.end(), it);
+ }
+
+ set.erase(w0.get());
+ set.erase(w1.get());
+ set.erase(w2.get());
+
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ {
+ list_set<Wrapped<int>*>::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+}
+
+template <typename T>
+class RefCounted : public base::RefCounted<RefCounted<T> > {
+ public:
+ RefCounted(const T& value) : value_(value) {}
+ T value() { return value_; }
+
+ private:
+ virtual ~RefCounted() {}
+ friend class base::RefCounted<RefCounted<T> >;
+ T value_;
+};
+
+TEST(ListSetTest, ListSetRefCounted) {
+ list_set<scoped_refptr<RefCounted<int> > > set;
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ {
+ list_set<scoped_refptr<RefCounted<int> > >::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+
+ scoped_refptr<RefCounted<int> > r0(new RefCounted<int>(0));
+ scoped_refptr<RefCounted<int> > r1(new RefCounted<int>(1));
+ scoped_refptr<RefCounted<int> > r2(new RefCounted<int>(2));
+
+ set.insert(r0);
+ set.insert(r1);
+ set.insert(r2);
+
+ EXPECT_EQ(static_cast<size_t>(3), set.size());
+
+ {
+ list_set<scoped_refptr<RefCounted<int> > >::iterator it = set.begin();
+ EXPECT_EQ(0, (*it)->value());
+ ++it;
+ EXPECT_EQ(1, (*it)->value());
+ ++it;
+ EXPECT_EQ(2, (*it)->value());
+ ++it;
+ EXPECT_EQ(set.end(), it);
+ }
+
+ set.erase(r0);
+ set.erase(r1);
+ set.erase(r2);
+
+ EXPECT_EQ(static_cast<size_t>(0), set.size());
+ {
+ list_set<scoped_refptr<RefCounted<int> > >::iterator it = set.begin();
+ EXPECT_EQ(set.end(), it);
+ }
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/webidbcursor_impl.cc b/content/browser/indexed_db/webidbcursor_impl.cc
new file mode 100644
index 0000000..629ebaf
--- /dev/null
+++ b/content/browser/indexed_db/webidbcursor_impl.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/webidbcursor_impl.h"
+
+#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_cursor.h"
+#include "content/common/indexed_db/indexed_db_key.h"
+#include "third_party/WebKit/public/platform/WebIDBKey.h"
+
+using WebKit::WebIDBCallbacks;
+
+namespace content {
+
+WebIDBCursorImpl::WebIDBCursorImpl(
+ scoped_refptr<IndexedDBCursor> idb_cursor_backend)
+ : idb_cursor_backend_(idb_cursor_backend) {}
+
+WebIDBCursorImpl::~WebIDBCursorImpl() {}
+
+void WebIDBCursorImpl::advance(unsigned long count,
+ WebIDBCallbacks* callbacks) {
+ idb_cursor_backend_->Advance(count,
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBCursorImpl::continueFunction(const WebKit::WebIDBKey& key,
+ WebIDBCallbacks* callbacks) {
+ idb_cursor_backend_->ContinueFunction(
+ make_scoped_ptr(new IndexedDBKey(key)),
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBCursorImpl::deleteFunction(WebIDBCallbacks* callbacks) {
+ idb_cursor_backend_->DeleteFunction(
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBCursorImpl::prefetchContinue(int number_to_fetch,
+ WebKit::WebIDBCallbacks* callbacks) {
+ idb_cursor_backend_->PrefetchContinue(
+ number_to_fetch, IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBCursorImpl::prefetchReset(int used_prefetches,
+ int unused_prefetches) {
+ idb_cursor_backend_->PrefetchReset(used_prefetches, unused_prefetches);
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/webidbcursor_impl.h b/content/browser/indexed_db/webidbcursor_impl.h
new file mode 100644
index 0000000..ec89877
--- /dev/null
+++ b/content/browser/indexed_db/webidbcursor_impl.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_WEBIDBCURSOR_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_WEBIDBCURSOR_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "third_party/WebKit/public/platform/WebIDBCursor.h"
+
+namespace content {
+class IndexedDBCursor;
+
+class WebIDBCursorImpl : public WebKit::WebIDBCursor {
+ public:
+ explicit WebIDBCursorImpl(scoped_refptr<IndexedDBCursor> cursor);
+ virtual ~WebIDBCursorImpl();
+
+ virtual void advance(unsigned long, WebKit::WebIDBCallbacks* callbacks);
+ virtual void continueFunction(const WebKit::WebIDBKey& key,
+ WebKit::WebIDBCallbacks* callbacks);
+ virtual void deleteFunction(WebKit::WebIDBCallbacks* callbacks);
+ virtual void prefetchContinue(int number_to_fetch,
+ WebKit::WebIDBCallbacks* callbacks);
+ virtual void prefetchReset(int used_prefetches, int unused_prefetches);
+
+ private:
+ scoped_refptr<IndexedDBCursor> idb_cursor_backend_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_WEBIDBCURSOR_IMPL_H_
diff --git a/content/browser/indexed_db/webidbdatabase_impl.cc b/content/browser/indexed_db/webidbdatabase_impl.cc
new file mode 100644
index 0000000..7c2c7e3
--- /dev/null
+++ b/content/browser/indexed_db/webidbdatabase_impl.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/webidbdatabase_impl.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_cursor.h"
+#include "content/browser/indexed_db/indexed_db_database.h"
+#include "content/browser/indexed_db/indexed_db_metadata.h"
+#include "content/common/indexed_db/indexed_db_key_range.h"
+#include "third_party/WebKit/public/platform/WebData.h"
+#include "third_party/WebKit/public/platform/WebIDBCallbacks.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseCallbacks.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseError.h"
+#include "third_party/WebKit/public/platform/WebIDBKey.h"
+#include "third_party/WebKit/public/platform/WebIDBKeyRange.h"
+#include "third_party/WebKit/public/platform/WebIDBMetadata.h"
+
+using WebKit::WebString;
+using WebKit::WebIDBKey;
+using WebKit::WebData;
+using WebKit::WebIDBKeyPath;
+using WebKit::WebIDBKeyRange;
+using WebKit::WebIDBDatabaseCallbacks;
+using WebKit::WebIDBCallbacks;
+using WebKit::WebVector;
+using WebKit::WebIDBDatabaseError;
+
+namespace content {
+
+WebIDBDatabaseImpl::WebIDBDatabaseImpl(
+ scoped_refptr<IndexedDBDatabase> database_backend,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks)
+ : database_backend_(database_backend),
+ database_callbacks_(database_callbacks) {}
+
+WebIDBDatabaseImpl::~WebIDBDatabaseImpl() {}
+
+void WebIDBDatabaseImpl::createObjectStore(long long transaction_id,
+ long long object_store_id,
+ const WebString& name,
+ const WebIDBKeyPath& key_path,
+ bool auto_increment) {
+ database_backend_->CreateObjectStore(transaction_id,
+ object_store_id,
+ name,
+ IndexedDBKeyPath(key_path),
+ auto_increment);
+}
+
+void WebIDBDatabaseImpl::deleteObjectStore(long long transaction_id,
+ long long object_store_id) {
+ database_backend_->DeleteObjectStore(transaction_id, object_store_id);
+}
+
+void WebIDBDatabaseImpl::createTransaction(
+ long long id,
+ WebIDBDatabaseCallbacks* /*callbacks*/,
+ const WebVector<long long>& object_store_ids,
+ unsigned short mode) {
+ if (!database_callbacks_)
+ return;
+ std::vector<int64> object_store_id_list(object_store_ids.size());
+ for (size_t i = 0; i < object_store_ids.size(); ++i)
+ object_store_id_list[i] = object_store_ids[i];
+ database_backend_->CreateTransaction(
+ id, database_callbacks_.get(), object_store_id_list, mode);
+}
+
+void WebIDBDatabaseImpl::close() {
+ // Use the callbacks passed in to the constructor so that the backend in
+ // multi-process chromium knows which database connection is closing.
+ if (!database_callbacks_)
+ return;
+ database_backend_->Close(database_callbacks_);
+ database_callbacks_ = NULL;
+}
+
+void WebIDBDatabaseImpl::forceClose() {
+ if (!database_callbacks_)
+ return;
+ database_backend_->Close(database_callbacks_);
+ database_callbacks_->OnForcedClose();
+ database_callbacks_ = NULL;
+}
+
+void WebIDBDatabaseImpl::abort(long long transaction_id) {
+ if (database_backend_)
+ database_backend_->Abort(transaction_id);
+}
+
+void WebIDBDatabaseImpl::abort(long long transaction_id,
+ const WebIDBDatabaseError& error) {
+ if (database_backend_)
+ database_backend_->Abort(transaction_id,
+ IndexedDBDatabaseError::Create(error));
+}
+
+void WebIDBDatabaseImpl::commit(long long transaction_id) {
+ if (database_backend_)
+ database_backend_->Commit(transaction_id);
+}
+
+void WebIDBDatabaseImpl::openCursor(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebIDBKeyRange& key_range,
+ unsigned short direction,
+ bool key_only,
+ TaskType task_type,
+ WebIDBCallbacks* callbacks) {
+ if (database_backend_)
+ database_backend_->OpenCursor(
+ transaction_id,
+ object_store_id,
+ index_id,
+ make_scoped_ptr(new IndexedDBKeyRange(key_range)),
+ static_cast<indexed_db::CursorDirection>(direction),
+ key_only,
+ static_cast<IndexedDBDatabase::TaskType>(task_type),
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBDatabaseImpl::count(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebIDBKeyRange& key_range,
+ WebIDBCallbacks* callbacks) {
+ if (database_backend_)
+ database_backend_->Count(transaction_id,
+ object_store_id,
+ index_id,
+ make_scoped_ptr(new IndexedDBKeyRange(key_range)),
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBDatabaseImpl::get(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebIDBKeyRange& key_range,
+ bool key_only,
+ WebIDBCallbacks* callbacks) {
+ if (database_backend_)
+ database_backend_->Get(transaction_id,
+ object_store_id,
+ index_id,
+ make_scoped_ptr(new IndexedDBKeyRange(key_range)),
+ key_only,
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBDatabaseImpl::put(long long transaction_id,
+ long long object_store_id,
+ const WebData& value,
+ const WebIDBKey& key,
+ PutMode put_mode,
+ WebIDBCallbacks* callbacks,
+ const WebVector<long long>& web_index_ids,
+ const WebVector<WebIndexKeys>& web_index_keys) {
+ if (!database_backend_)
+ return;
+
+ DCHECK_EQ(web_index_ids.size(), web_index_keys.size());
+ std::vector<int64> index_ids(web_index_ids.size());
+ std::vector<IndexedDBDatabase::IndexKeys> index_keys(web_index_keys.size());
+
+ for (size_t i = 0; i < web_index_ids.size(); ++i) {
+ index_ids[i] = web_index_ids[i];
+ IndexedDBKey::KeyArray index_key_list;
+ for (size_t j = 0; j < web_index_keys[i].size(); ++j)
+ index_key_list.push_back(IndexedDBKey(web_index_keys[i][j]));
+ index_keys[i] = index_key_list;
+ }
+
+ std::vector<char> value_buffer(value.data(), value.data() + value.size());
+ database_backend_->Put(transaction_id,
+ object_store_id,
+ &value_buffer,
+ make_scoped_ptr(new IndexedDBKey(key)),
+ static_cast<IndexedDBDatabase::PutMode>(put_mode),
+ IndexedDBCallbacksWrapper::Create(callbacks),
+ index_ids,
+ index_keys);
+}
+
+void WebIDBDatabaseImpl::setIndexKeys(
+ long long transaction_id,
+ long long object_store_id,
+ const WebIDBKey& primary_key,
+ const WebVector<long long>& web_index_ids,
+ const WebVector<WebIndexKeys>& web_index_keys) {
+ if (!database_backend_)
+ return;
+
+ DCHECK_EQ(web_index_ids.size(), web_index_keys.size());
+ std::vector<int64> index_ids(web_index_ids.size());
+ std::vector<IndexedDBDatabase::IndexKeys> index_keys(web_index_keys.size());
+
+ for (size_t i = 0; i < web_index_ids.size(); ++i) {
+ index_ids[i] = web_index_ids[i];
+ IndexedDBKey::KeyArray index_key_list;
+ for (size_t j = 0; j < web_index_keys[i].size(); ++j)
+ index_key_list.push_back(IndexedDBKey(web_index_keys[i][j]));
+ index_keys[i] = index_key_list;
+ }
+ database_backend_->SetIndexKeys(
+ transaction_id,
+ object_store_id,
+ make_scoped_ptr(new IndexedDBKey(primary_key)),
+ index_ids,
+ index_keys);
+}
+
+void WebIDBDatabaseImpl::setIndexesReady(
+ long long transaction_id,
+ long long object_store_id,
+ const WebVector<long long>& web_index_ids) {
+ if (!database_backend_)
+ return;
+
+ std::vector<int64> index_ids(web_index_ids.size());
+ for (size_t i = 0; i < web_index_ids.size(); ++i)
+ index_ids[i] = web_index_ids[i];
+ database_backend_->SetIndexesReady(
+ transaction_id, object_store_id, index_ids);
+}
+
+void WebIDBDatabaseImpl::deleteRange(long long transaction_id,
+ long long object_store_id,
+ const WebIDBKeyRange& key_range,
+ WebIDBCallbacks* callbacks) {
+ if (database_backend_)
+ database_backend_->DeleteRange(
+ transaction_id,
+ object_store_id,
+ make_scoped_ptr(new IndexedDBKeyRange(key_range)),
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBDatabaseImpl::clear(long long transaction_id,
+ long long object_store_id,
+ WebIDBCallbacks* callbacks) {
+ if (database_backend_)
+ database_backend_->Clear(transaction_id,
+ object_store_id,
+ IndexedDBCallbacksWrapper::Create(callbacks));
+}
+
+void WebIDBDatabaseImpl::createIndex(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebString& name,
+ const WebIDBKeyPath& key_path,
+ bool unique,
+ bool multi_entry) {
+ if (database_backend_)
+ database_backend_->CreateIndex(transaction_id,
+ object_store_id,
+ index_id,
+ name,
+ IndexedDBKeyPath(key_path),
+ unique,
+ multi_entry);
+}
+
+void WebIDBDatabaseImpl::deleteIndex(long long transaction_id,
+ long long object_store_id,
+ long long index_id) {
+ if (database_backend_)
+ database_backend_->DeleteIndex(transaction_id, object_store_id, index_id);
+}
+
+} // namespace WebKit
diff --git a/content/browser/indexed_db/webidbdatabase_impl.h b/content/browser/indexed_db/webidbdatabase_impl.h
new file mode 100644
index 0000000..52c137f
--- /dev/null
+++ b/content/browser/indexed_db/webidbdatabase_impl.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_WEBIDBDATABASE_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_WEBIDBDATABASE_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/browser/indexed_db/indexed_db_database.h"
+#include "content/browser/indexed_db/indexed_db_database_callbacks_wrapper.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabase.h"
+
+namespace WebKit {
+class WebIDBDatabaseCallbacks;
+class WebIDBDatabaseError;
+class WebIDBDatabaseMetadata;
+}
+
+namespace content {
+class IndexedDBDatabase;
+class IndexedDBDatabaseCallbacksWrapper;
+
+// See comment in WebIDBFactory for a high level overview these classes.
+class WebIDBDatabaseImpl : public WebKit::WebIDBDatabase {
+ public:
+ WebIDBDatabaseImpl(
+ scoped_refptr<IndexedDBDatabase> db,
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks);
+ virtual ~WebIDBDatabaseImpl();
+
+ virtual void createObjectStore(long long transaction_id,
+ long long object_store_id,
+ const WebKit::WebString& name,
+ const WebKit::WebIDBKeyPath& key_path,
+ bool auto_increment);
+ virtual void deleteObjectStore(long long object_store_id,
+ long long transaction_id);
+ virtual void createTransaction(long long id,
+ WebKit::WebIDBDatabaseCallbacks* callbacks,
+ const WebKit::WebVector<long long>& scope,
+ unsigned short mode);
+ virtual void forceClose();
+ virtual void close();
+ virtual void abort(long long transaction_id);
+ virtual void abort(long long transaction_id,
+ const WebKit::WebIDBDatabaseError& error);
+ virtual void commit(long long transaction_id);
+
+ virtual void get(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebKit::WebIDBKeyRange& range,
+ bool key_only,
+ WebKit::WebIDBCallbacks* callbacks);
+ virtual void put(long long transaction_id,
+ long long object_store_id,
+ const WebKit::WebData& value,
+ const WebKit::WebIDBKey& key,
+ PutMode mode,
+ WebKit::WebIDBCallbacks* callbacks,
+ const WebKit::WebVector<long long>& index_ids,
+ const WebKit::WebVector<WebIndexKeys>& index_keys);
+ virtual void setIndexKeys(long long transaction_id,
+ long long object_store_id,
+ const WebKit::WebIDBKey& key,
+ const WebKit::WebVector<long long>& index_ids,
+ const WebKit::WebVector<WebIndexKeys>& index_keys);
+ virtual void setIndexesReady(long long transaction_id,
+ long long object_store_id,
+ const WebKit::WebVector<long long>& index_ids);
+ virtual void openCursor(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebKit::WebIDBKeyRange& range,
+ unsigned short direction,
+ bool key_only,
+ TaskType task_type,
+ WebKit::WebIDBCallbacks* callbacks);
+ virtual void count(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebKit::WebIDBKeyRange& range,
+ WebKit::WebIDBCallbacks* callbacks);
+ virtual void deleteRange(long long transaction_id,
+ long long object_store_id,
+ const WebKit::WebIDBKeyRange& range,
+ WebKit::WebIDBCallbacks* callbacks);
+ virtual void clear(long long transaction_id,
+ long long object_store_id,
+ WebKit::WebIDBCallbacks* callbacks);
+
+ virtual void createIndex(long long transaction_id,
+ long long object_store_id,
+ long long index_id,
+ const WebKit::WebString& name,
+ const WebKit::WebIDBKeyPath& key_path,
+ bool unique,
+ bool multi_entry);
+ virtual void deleteIndex(long long transaction_id,
+ long long object_store_id,
+ long long index_id);
+
+ private:
+ scoped_refptr<IndexedDBDatabase> database_backend_;
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_WEBIDBDATABASE_IMPL_H_
diff --git a/content/browser/indexed_db/webidbfactory_impl.cc b/content/browser/indexed_db/webidbfactory_impl.cc
new file mode 100644
index 0000000..1fc78fa
--- /dev/null
+++ b/content/browser/indexed_db/webidbfactory_impl.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/webidbfactory_impl.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/indexed_db/indexed_db_callbacks_wrapper.h"
+#include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/browser/indexed_db/indexed_db_factory_impl.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseCallbacks.h"
+#include "third_party/WebKit/public/platform/WebIDBDatabaseError.h"
+
+using WebKit::WebIDBCallbacks;
+using WebKit::WebIDBDatabaseCallbacks;
+using WebKit::WebIDBFactory;
+using WebKit::WebString;
+
+namespace content {
+
+WebIDBFactoryImpl::WebIDBFactoryImpl()
+ : idb_factory_backend_(IndexedDBFactoryImpl::Create()) {}
+
+WebIDBFactoryImpl::~WebIDBFactoryImpl() {}
+
+void WebIDBFactoryImpl::getDatabaseNames(WebIDBCallbacks* callbacks,
+ const WebString& database_identifier,
+ const WebString& data_dir) {
+ idb_factory_backend_->GetDatabaseNames(
+ IndexedDBCallbacksWrapper::Create(callbacks),
+ database_identifier,
+ data_dir);
+}
+
+void WebIDBFactoryImpl::open(const WebString& name,
+ long long version,
+ long long transaction_id,
+ WebIDBCallbacks* callbacks,
+ WebIDBDatabaseCallbacks* database_callbacks,
+ const WebString& database_identifier,
+ const WebString& data_dir) {
+ scoped_refptr<IndexedDBCallbacksWrapper> callbacks_proxy =
+ IndexedDBCallbacksWrapper::Create(callbacks);
+ scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_proxy =
+ IndexedDBDatabaseCallbacksWrapper::Create(database_callbacks);
+
+ callbacks_proxy->SetDatabaseCallbacks(database_callbacks_proxy);
+ idb_factory_backend_->Open(name,
+ version,
+ transaction_id,
+ callbacks_proxy.get(),
+ database_callbacks_proxy.get(),
+ database_identifier,
+ data_dir);
+}
+
+void WebIDBFactoryImpl::deleteDatabase(const WebString& name,
+ WebIDBCallbacks* callbacks,
+ const WebString& database_identifier,
+ const WebString& data_dir) {
+ idb_factory_backend_->DeleteDatabase(
+ name,
+ IndexedDBCallbacksWrapper::Create(callbacks),
+ database_identifier,
+ data_dir);
+}
+
+} // namespace WebKit
diff --git a/content/browser/indexed_db/webidbfactory_impl.h b/content/browser/indexed_db/webidbfactory_impl.h
new file mode 100644
index 0000000..3835285
--- /dev/null
+++ b/content/browser/indexed_db/webidbfactory_impl.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_WEBIDBFACTORY_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_WEBIDBFACTORY_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "third_party/WebKit/public/platform/WebIDBFactory.h"
+
+namespace content {
+
+class IndexedDBFactory;
+
+class WebIDBFactoryImpl : public WebKit::WebIDBFactory {
+ public:
+ WebIDBFactoryImpl();
+ virtual ~WebIDBFactoryImpl();
+
+ virtual void getDatabaseNames(WebKit::WebIDBCallbacks* callbacks,
+ const WebKit::WebString& database_identifier,
+ const WebKit::WebString& data_dir);
+ virtual void open(const WebKit::WebString& name,
+ long long version,
+ long long transaction_id,
+ WebKit::WebIDBCallbacks* callbacks,
+ WebKit::WebIDBDatabaseCallbacks* database_callbacks,
+ const WebKit::WebString& database_identifier,
+ const WebKit::WebString& data_dir);
+ virtual void deleteDatabase(const WebKit::WebString& name,
+ WebKit::WebIDBCallbacks* callbacks,
+ const WebKit::WebString& database_identifier,
+ const WebKit::WebString& data_dir);
+
+ private:
+ scoped_refptr<IndexedDBFactory> idb_factory_backend_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_WEBIDBFACTORY_IMPL_H_