Home | History | Annotate | Download | only in indexed_db
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/files/file_path.h"
     10 #include "base/process/process.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "content/browser/indexed_db/indexed_db_callbacks.h"
     13 #include "content/browser/indexed_db/indexed_db_connection.h"
     14 #include "content/browser/indexed_db/indexed_db_context_impl.h"
     15 #include "content/browser/indexed_db/indexed_db_cursor.h"
     16 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
     17 #include "content/browser/indexed_db/indexed_db_metadata.h"
     18 #include "content/browser/renderer_host/render_message_filter.h"
     19 #include "content/common/indexed_db/indexed_db_messages.h"
     20 #include "content/public/browser/browser_thread.h"
     21 #include "content/public/browser/user_metrics.h"
     22 #include "content/public/common/content_switches.h"
     23 #include "content/public/common/result_codes.h"
     24 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
     25 #include "url/gurl.h"
     26 #include "webkit/browser/database/database_util.h"
     27 #include "webkit/common/database/database_identifier.h"
     28 
     29 using webkit_database::DatabaseUtil;
     30 using WebKit::WebIDBKey;
     31 
     32 namespace content {
     33 
     34 IndexedDBDispatcherHost::IndexedDBDispatcherHost(
     35     int ipc_process_id,
     36     IndexedDBContextImpl* indexed_db_context)
     37     : indexed_db_context_(indexed_db_context),
     38       database_dispatcher_host_(new DatabaseDispatcherHost(this)),
     39       cursor_dispatcher_host_(new CursorDispatcherHost(this)),
     40       ipc_process_id_(ipc_process_id) {
     41   DCHECK(indexed_db_context_);
     42 }
     43 
     44 IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {}
     45 
     46 void IndexedDBDispatcherHost::OnChannelClosing() {
     47   BrowserMessageFilter::OnChannelClosing();
     48 
     49   bool success = indexed_db_context_->TaskRunner()->PostTask(
     50       FROM_HERE,
     51       base::Bind(&IndexedDBDispatcherHost::ResetDispatcherHosts, this));
     52 
     53   if (!success)
     54     ResetDispatcherHosts();
     55 }
     56 
     57 void IndexedDBDispatcherHost::OnDestruct() const {
     58   // The last reference to the dispatcher may be a posted task, which would
     59   // be destructed on the IndexedDB thread. Without this override, that would
     60   // take the dispatcher with it. Since the dispatcher may be keeping the
     61   // IndexedDBContext alive, it might be destructed to on its own thread,
     62   // which is not supported. Ensure destruction runs on the IO thread instead.
     63   BrowserThread::DeleteOnIOThread::Destruct(this);
     64 }
     65 
     66 void IndexedDBDispatcherHost::ResetDispatcherHosts() {
     67   // It is important that the various *_dispatcher_host_ members are reset
     68   // on the IndexedDB thread, since there might be incoming messages on that
     69   // thread, and we must not reset the dispatcher hosts until after those
     70   // messages are processed.
     71   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
     72 
     73   // Note that we explicitly separate CloseAll() from destruction of the
     74   // DatabaseDispatcherHost, since CloseAll() can invoke callbacks which need to
     75   // be dispatched through database_dispatcher_host_.
     76   database_dispatcher_host_->CloseAll();
     77   database_dispatcher_host_.reset();
     78   cursor_dispatcher_host_.reset();
     79 }
     80 
     81 base::TaskRunner* IndexedDBDispatcherHost::OverrideTaskRunnerForMessage(
     82     const IPC::Message& message) {
     83   if (IPC_MESSAGE_CLASS(message) == IndexedDBMsgStart)
     84     return indexed_db_context_->TaskRunner();
     85   return NULL;
     86 }
     87 
     88 bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message& message,
     89                                                 bool* message_was_ok) {
     90   if (IPC_MESSAGE_CLASS(message) != IndexedDBMsgStart)
     91     return false;
     92 
     93   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
     94 
     95   bool handled =
     96       database_dispatcher_host_->OnMessageReceived(message, message_was_ok) ||
     97       cursor_dispatcher_host_->OnMessageReceived(message, message_was_ok);
     98 
     99   if (!handled) {
    100     handled = true;
    101     IPC_BEGIN_MESSAGE_MAP_EX(IndexedDBDispatcherHost, message, *message_was_ok)
    102       IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryGetDatabaseNames,
    103                           OnIDBFactoryGetDatabaseNames)
    104       IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryOpen, OnIDBFactoryOpen)
    105       IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryDeleteDatabase,
    106                           OnIDBFactoryDeleteDatabase)
    107       IPC_MESSAGE_UNHANDLED(handled = false)
    108     IPC_END_MESSAGE_MAP()
    109   }
    110   return handled;
    111 }
    112 
    113 int32 IndexedDBDispatcherHost::Add(IndexedDBCursor* cursor) {
    114   if (!cursor_dispatcher_host_) {
    115     return 0;
    116   }
    117   return cursor_dispatcher_host_->map_.Add(cursor);
    118 }
    119 
    120 int32 IndexedDBDispatcherHost::Add(IndexedDBConnection* connection,
    121                                    int32 ipc_thread_id,
    122                                    const GURL& origin_url) {
    123   if (!database_dispatcher_host_) {
    124     connection->Close();
    125     delete connection;
    126     return -1;
    127   }
    128   int32 ipc_database_id = database_dispatcher_host_->map_.Add(connection);
    129   Context()->ConnectionOpened(origin_url, connection);
    130   database_dispatcher_host_->database_url_map_[ipc_database_id] = origin_url;
    131   return ipc_database_id;
    132 }
    133 
    134 void IndexedDBDispatcherHost::RegisterTransactionId(int64 host_transaction_id,
    135                                                     const GURL& url) {
    136   if (!database_dispatcher_host_)
    137     return;
    138   database_dispatcher_host_->transaction_url_map_[host_transaction_id] = url;
    139 }
    140 
    141 int64 IndexedDBDispatcherHost::HostTransactionId(int64 transaction_id) {
    142   // Inject the renderer process id into the transaction id, to
    143   // uniquely identify this transaction, and effectively bind it to
    144   // the renderer that initiated it. The lower 32 bits of
    145   // transaction_id are guaranteed to be unique within that renderer.
    146   base::ProcessId pid = peer_pid();
    147   DCHECK(!(transaction_id >> 32)) << "Transaction ids can only be 32 bits";
    148   COMPILE_ASSERT(sizeof(base::ProcessId) <= sizeof(int32),
    149                  Process_ID_must_fit_in_32_bits);
    150 
    151   return transaction_id | (static_cast<uint64>(pid) << 32);
    152 }
    153 
    154 int64 IndexedDBDispatcherHost::RendererTransactionId(
    155     int64 host_transaction_id) {
    156   DCHECK(host_transaction_id >> 32 == peer_pid())
    157       << "Invalid renderer target for transaction id";
    158   return host_transaction_id & 0xffffffff;
    159 }
    160 
    161 IndexedDBCursor* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id) {
    162   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    163   return cursor_dispatcher_host_->map_.Lookup(ipc_cursor_id);
    164 }
    165 
    166 ::IndexedDBDatabaseMetadata IndexedDBDispatcherHost::ConvertMetadata(
    167     const content::IndexedDBDatabaseMetadata& web_metadata) {
    168   ::IndexedDBDatabaseMetadata metadata;
    169   metadata.id = web_metadata.id;
    170   metadata.name = web_metadata.name;
    171   metadata.version = web_metadata.version;
    172   metadata.int_version = web_metadata.int_version;
    173   metadata.max_object_store_id = web_metadata.max_object_store_id;
    174 
    175   for (content::IndexedDBDatabaseMetadata::ObjectStoreMap::const_iterator iter =
    176            web_metadata.object_stores.begin();
    177        iter != web_metadata.object_stores.end();
    178        ++iter) {
    179 
    180     const content::IndexedDBObjectStoreMetadata& web_store_metadata =
    181         iter->second;
    182     ::IndexedDBObjectStoreMetadata idb_store_metadata;
    183     idb_store_metadata.id = web_store_metadata.id;
    184     idb_store_metadata.name = web_store_metadata.name;
    185     idb_store_metadata.keyPath = web_store_metadata.key_path;
    186     idb_store_metadata.autoIncrement = web_store_metadata.auto_increment;
    187     idb_store_metadata.max_index_id = web_store_metadata.max_index_id;
    188 
    189     for (content::IndexedDBObjectStoreMetadata::IndexMap::const_iterator
    190              index_iter = web_store_metadata.indexes.begin();
    191          index_iter != web_store_metadata.indexes.end();
    192          ++index_iter) {
    193       const content::IndexedDBIndexMetadata& web_index_metadata =
    194           index_iter->second;
    195       ::IndexedDBIndexMetadata idb_index_metadata;
    196       idb_index_metadata.id = web_index_metadata.id;
    197       idb_index_metadata.name = web_index_metadata.name;
    198       idb_index_metadata.keyPath = web_index_metadata.key_path;
    199       idb_index_metadata.unique = web_index_metadata.unique;
    200       idb_index_metadata.multiEntry = web_index_metadata.multi_entry;
    201       idb_store_metadata.indexes.push_back(idb_index_metadata);
    202     }
    203     metadata.object_stores.push_back(idb_store_metadata);
    204   }
    205   return metadata;
    206 }
    207 
    208 void IndexedDBDispatcherHost::OnIDBFactoryGetDatabaseNames(
    209     const IndexedDBHostMsg_FactoryGetDatabaseNames_Params& params) {
    210   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    211   base::FilePath indexed_db_path = indexed_db_context_->data_path();
    212 
    213   Context()->GetIDBFactory()->GetDatabaseNames(
    214       new IndexedDBCallbacks(
    215           this, params.ipc_thread_id, params.ipc_callbacks_id),
    216       params.database_identifier,
    217       indexed_db_path);
    218 }
    219 
    220 void IndexedDBDispatcherHost::OnIDBFactoryOpen(
    221     const IndexedDBHostMsg_FactoryOpen_Params& params) {
    222   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    223   base::FilePath indexed_db_path = indexed_db_context_->data_path();
    224 
    225   GURL origin_url =
    226       webkit_database::GetOriginFromIdentifier(params.database_identifier);
    227 
    228   int64 host_transaction_id = HostTransactionId(params.transaction_id);
    229 
    230   // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
    231   // created) if this origin is already over quota.
    232   scoped_refptr<IndexedDBCallbacks> callbacks =
    233       new IndexedDBCallbacks(this,
    234                              params.ipc_thread_id,
    235                              params.ipc_callbacks_id,
    236                              params.ipc_database_callbacks_id,
    237                              host_transaction_id,
    238                              origin_url);
    239   scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks =
    240       new IndexedDBDatabaseCallbacks(
    241           this, params.ipc_thread_id, params.ipc_database_callbacks_id);
    242   Context()->GetIDBFactory()->Open(params.name,
    243                                    params.version,
    244                                    host_transaction_id,
    245                                    callbacks,
    246                                    database_callbacks,
    247                                    params.database_identifier,
    248                                    indexed_db_path);
    249 }
    250 
    251 void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
    252     const IndexedDBHostMsg_FactoryDeleteDatabase_Params& params) {
    253   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    254   base::FilePath indexed_db_path = indexed_db_context_->data_path();
    255   Context()->GetIDBFactory()->DeleteDatabase(
    256       params.name,
    257       new IndexedDBCallbacks(
    258           this, params.ipc_thread_id, params.ipc_callbacks_id),
    259       params.database_identifier,
    260       indexed_db_path);
    261 }
    262 
    263 void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id,
    264                                                 bool committed) {
    265   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    266   if (!database_dispatcher_host_)
    267     return;
    268   TransactionIDToURLMap& transaction_url_map =
    269       database_dispatcher_host_->transaction_url_map_;
    270   TransactionIDToSizeMap& transaction_size_map =
    271       database_dispatcher_host_->transaction_size_map_;
    272   TransactionIDToDatabaseIDMap& transaction_database_map =
    273       database_dispatcher_host_->transaction_database_map_;
    274   if (committed)
    275     Context()->TransactionComplete(transaction_url_map[host_transaction_id]);
    276   // It's unclear if std::map::erase(key) has defined behavior if the
    277   // key is not found.
    278   // TODO(alecflett): Remove if it is proven that it is safe.
    279   if (transaction_url_map.find(host_transaction_id) !=
    280       transaction_url_map.end())
    281     transaction_url_map.erase(host_transaction_id);
    282   if (transaction_size_map.find(host_transaction_id) !=
    283       transaction_size_map.end())
    284     transaction_size_map.erase(host_transaction_id);
    285   if (transaction_database_map.find(host_transaction_id) !=
    286       transaction_database_map.end())
    287     transaction_database_map.erase(host_transaction_id);
    288 }
    289 
    290 //////////////////////////////////////////////////////////////////////
    291 // Helper templates.
    292 //
    293 
    294 template <typename ObjectType>
    295 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
    296     IDMap<ObjectType, IDMapOwnPointer>* map,
    297     int32 ipc_return_object_id) {
    298   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    299   ObjectType* return_object = map->Lookup(ipc_return_object_id);
    300   if (!return_object) {
    301     NOTREACHED() << "Uh oh, couldn't find object with id "
    302                  << ipc_return_object_id;
    303     RecordAction(UserMetricsAction("BadMessageTerminate_IDBMF"));
    304     BadMessageReceived();
    305   }
    306   return return_object;
    307 }
    308 
    309 template <typename ObjectType>
    310 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
    311     RefIDMap<ObjectType>* map,
    312     int32 ipc_return_object_id) {
    313   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    314   ObjectType* return_object = map->Lookup(ipc_return_object_id);
    315   if (!return_object) {
    316     NOTREACHED() << "Uh oh, couldn't find object with id "
    317                  << ipc_return_object_id;
    318     RecordAction(UserMetricsAction("BadMessageTerminate_IDBMF"));
    319     BadMessageReceived();
    320   }
    321   return return_object;
    322 }
    323 
    324 template <typename MapType>
    325 void IndexedDBDispatcherHost::DestroyObject(MapType* map, int32 ipc_object_id) {
    326   GetOrTerminateProcess(map, ipc_object_id);
    327   map->Remove(ipc_object_id);
    328 }
    329 
    330 //////////////////////////////////////////////////////////////////////
    331 // IndexedDBDispatcherHost::DatabaseDispatcherHost
    332 //
    333 
    334 IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost(
    335     IndexedDBDispatcherHost* parent)
    336     : parent_(parent) {
    337   map_.set_check_on_null_data(true);
    338 }
    339 
    340 IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() {
    341   // TODO(alecflett): uncomment these when we find the source of these leaks.
    342   // DCHECK(transaction_size_map_.empty());
    343   // DCHECK(transaction_url_map_.empty());
    344 }
    345 
    346 void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
    347   DCHECK(
    348       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    349   // Abort outstanding transactions started by connections in the associated
    350   // front-end to unblock later transactions. This should only occur on unclean
    351   // (crash) or abrupt (process-kill) shutdowns.
    352   for (TransactionIDToDatabaseIDMap::iterator iter =
    353            transaction_database_map_.begin();
    354        iter != transaction_database_map_.end();) {
    355     int64 transaction_id = iter->first;
    356     int32 ipc_database_id = iter->second;
    357     ++iter;
    358     IndexedDBConnection* connection = map_.Lookup(ipc_database_id);
    359     if (connection) {
    360       connection->database()->Abort(
    361           transaction_id,
    362           IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError));
    363     }
    364   }
    365   DCHECK(transaction_database_map_.empty());
    366 
    367   for (WebIDBObjectIDToURLMap::iterator iter = database_url_map_.begin();
    368        iter != database_url_map_.end();
    369        iter++) {
    370     IndexedDBConnection* connection = map_.Lookup(iter->first);
    371     if (connection) {
    372       connection->Close();
    373       parent_->Context()->ConnectionClosed(iter->second, connection);
    374     }
    375   }
    376 }
    377 
    378 bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
    379     const IPC::Message& message,
    380     bool* msg_is_ok) {
    381   DCHECK(
    382       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    383   bool handled = true;
    384   IPC_BEGIN_MESSAGE_MAP_EX(
    385       IndexedDBDispatcherHost::DatabaseDispatcherHost, message, *msg_is_ok)
    386     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore,
    387                         OnCreateObjectStore)
    388     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore,
    389                         OnDeleteObjectStore)
    390     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction,
    391                         OnCreateTransaction)
    392     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose)
    393     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed, OnDestroyed)
    394     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet, OnGet)
    395     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPut)
    396     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys, OnSetIndexKeys)
    397     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady,
    398                         OnSetIndexesReady)
    399     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseOpenCursor, OnOpenCursor)
    400     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCount, OnCount)
    401     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteRange, OnDeleteRange)
    402     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClear, OnClear)
    403     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateIndex, OnCreateIndex)
    404     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteIndex, OnDeleteIndex)
    405     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseAbort, OnAbort)
    406     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit, OnCommit)
    407     IPC_MESSAGE_UNHANDLED(handled = false)
    408   IPC_END_MESSAGE_MAP()
    409   return handled;
    410 }
    411 
    412 void IndexedDBDispatcherHost::DatabaseDispatcherHost::Send(
    413     IPC::Message* message) {
    414   parent_->Send(message);
    415 }
    416 
    417 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore(
    418     const IndexedDBHostMsg_DatabaseCreateObjectStore_Params& params) {
    419   DCHECK(
    420       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    421   IndexedDBConnection* connection =
    422       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    423   if (!connection)
    424     return;
    425 
    426   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
    427   connection->database()->CreateObjectStore(host_transaction_id,
    428                                             params.object_store_id,
    429                                             params.name,
    430                                             params.key_path,
    431                                             params.auto_increment);
    432   if (parent_->Context()->IsOverQuota(
    433           database_url_map_[params.ipc_database_id])) {
    434     connection->database()->Abort(
    435         host_transaction_id,
    436         IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError));
    437   }
    438 }
    439 
    440 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore(
    441     int32 ipc_database_id,
    442     int64 transaction_id,
    443     int64 object_store_id) {
    444   DCHECK(
    445       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    446   IndexedDBConnection* connection =
    447       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
    448   if (!connection)
    449     return;
    450 
    451   connection->database()->DeleteObjectStore(
    452       parent_->HostTransactionId(transaction_id), object_store_id);
    453 }
    454 
    455 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction(
    456     const IndexedDBHostMsg_DatabaseCreateTransaction_Params& params) {
    457   DCHECK(
    458       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    459   IndexedDBConnection* connection =
    460       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    461   if (!connection)
    462     return;
    463 
    464   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
    465 
    466   if (transaction_database_map_.find(host_transaction_id) !=
    467       transaction_database_map_.end()) {
    468     DLOG(ERROR) << "Duplicate host_transaction_id.";
    469     return;
    470   }
    471 
    472   connection->database()->CreateTransaction(
    473       host_transaction_id, connection, params.object_store_ids, params.mode);
    474   transaction_database_map_[host_transaction_id] = params.ipc_database_id;
    475   parent_->RegisterTransactionId(host_transaction_id,
    476                                  database_url_map_[params.ipc_database_id]);
    477 }
    478 
    479 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose(
    480     int32 ipc_database_id) {
    481   DCHECK(
    482       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    483   IndexedDBConnection* connection =
    484       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
    485   if (!connection)
    486     return;
    487   connection->Close();
    488 }
    489 
    490 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
    491     int32 ipc_object_id) {
    492   DCHECK(
    493       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    494   IndexedDBConnection* connection = map_.Lookup(ipc_object_id);
    495   parent_->Context()
    496       ->ConnectionClosed(database_url_map_[ipc_object_id], connection);
    497   database_url_map_.erase(ipc_object_id);
    498   parent_->DestroyObject(&map_, ipc_object_id);
    499 }
    500 
    501 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet(
    502     const IndexedDBHostMsg_DatabaseGet_Params& params) {
    503   DCHECK(
    504       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    505   IndexedDBConnection* connection =
    506       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    507   if (!connection)
    508     return;
    509 
    510   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
    511       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
    512   connection->database()->Get(
    513       parent_->HostTransactionId(params.transaction_id),
    514       params.object_store_id,
    515       params.index_id,
    516       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
    517       params.key_only,
    518       callbacks);
    519 }
    520 
    521 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
    522     const IndexedDBHostMsg_DatabasePut_Params& params) {
    523   DCHECK(
    524       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    525 
    526   IndexedDBConnection* connection =
    527       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    528   if (!connection)
    529     return;
    530   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
    531       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
    532 
    533   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
    534   // TODO(alecflett): Avoid a copy here.
    535   std::string value_copy(params.value);
    536   connection->database()->Put(
    537       host_transaction_id,
    538       params.object_store_id,
    539       &value_copy,
    540       make_scoped_ptr(new IndexedDBKey(params.key)),
    541       static_cast<IndexedDBDatabase::PutMode>(params.put_mode),
    542       callbacks,
    543       params.index_ids,
    544       params.index_keys);
    545   TransactionIDToSizeMap* map =
    546       &parent_->database_dispatcher_host_->transaction_size_map_;
    547   // Size can't be big enough to overflow because it represents the
    548   // actual bytes passed through IPC.
    549   (*map)[host_transaction_id] += params.value.size();
    550 }
    551 
    552 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys(
    553     const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params) {
    554   DCHECK(
    555       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    556   IndexedDBConnection* connection =
    557       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    558   if (!connection)
    559     return;
    560 
    561   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
    562   if (params.index_ids.size() != params.index_keys.size()) {
    563     connection->database()->Abort(
    564         host_transaction_id,
    565         IndexedDBDatabaseError(
    566             WebKit::WebIDBDatabaseExceptionUnknownError,
    567             "Malformed IPC message: index_ids.size() != index_keys.size()"));
    568     return;
    569   }
    570 
    571   connection->database()->SetIndexKeys(
    572       host_transaction_id,
    573       params.object_store_id,
    574       make_scoped_ptr(new IndexedDBKey(params.primary_key)),
    575       params.index_ids,
    576       params.index_keys);
    577 }
    578 
    579 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady(
    580     int32 ipc_database_id,
    581     int64 transaction_id,
    582     int64 object_store_id,
    583     const std::vector<int64>& index_ids) {
    584   DCHECK(
    585       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    586   IndexedDBConnection* connection =
    587       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
    588   if (!connection)
    589     return;
    590 
    591   connection->database()->SetIndexesReady(
    592       parent_->HostTransactionId(transaction_id), object_store_id, index_ids);
    593 }
    594 
    595 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpenCursor(
    596     const IndexedDBHostMsg_DatabaseOpenCursor_Params& params) {
    597   DCHECK(
    598       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    599   IndexedDBConnection* connection =
    600       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    601   if (!connection)
    602     return;
    603 
    604   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
    605       parent_, params.ipc_thread_id, params.ipc_callbacks_id, -1));
    606   connection->database()->OpenCursor(
    607       parent_->HostTransactionId(params.transaction_id),
    608       params.object_store_id,
    609       params.index_id,
    610       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
    611       static_cast<indexed_db::CursorDirection>(params.direction),
    612       params.key_only,
    613       static_cast<IndexedDBDatabase::TaskType>(params.task_type),
    614       callbacks);
    615 }
    616 
    617 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCount(
    618     const IndexedDBHostMsg_DatabaseCount_Params& params) {
    619   DCHECK(
    620       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    621   IndexedDBConnection* connection =
    622       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    623   if (!connection)
    624     return;
    625 
    626   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
    627       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
    628   connection->database()->Count(
    629       parent_->HostTransactionId(params.transaction_id),
    630       params.object_store_id,
    631       params.index_id,
    632       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
    633       callbacks);
    634 }
    635 
    636 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteRange(
    637     const IndexedDBHostMsg_DatabaseDeleteRange_Params& params) {
    638   DCHECK(
    639       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    640   IndexedDBConnection* connection =
    641       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    642   if (!connection)
    643     return;
    644 
    645   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
    646       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
    647   connection->database()->DeleteRange(
    648       parent_->HostTransactionId(params.transaction_id),
    649       params.object_store_id,
    650       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
    651       callbacks);
    652 }
    653 
    654 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear(
    655     int32 ipc_thread_id,
    656     int32 ipc_callbacks_id,
    657     int32 ipc_database_id,
    658     int64 transaction_id,
    659     int64 object_store_id) {
    660   DCHECK(
    661       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    662   IndexedDBConnection* connection =
    663       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
    664   if (!connection)
    665     return;
    666 
    667   scoped_refptr<IndexedDBCallbacks> callbacks(
    668       new IndexedDBCallbacks(parent_, ipc_thread_id, ipc_callbacks_id));
    669 
    670   connection->database()->Clear(
    671       parent_->HostTransactionId(transaction_id), object_store_id, callbacks);
    672 }
    673 
    674 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnAbort(
    675     int32 ipc_database_id,
    676     int64 transaction_id) {
    677   DCHECK(
    678       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    679   IndexedDBConnection* connection =
    680       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
    681   if (!connection)
    682     return;
    683 
    684   connection->database()->Abort(parent_->HostTransactionId(transaction_id));
    685 }
    686 
    687 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit(
    688     int32 ipc_database_id,
    689     int64 transaction_id) {
    690   DCHECK(
    691       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    692   IndexedDBConnection* connection =
    693       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
    694   if (!connection)
    695     return;
    696 
    697   int64 host_transaction_id = parent_->HostTransactionId(transaction_id);
    698   int64 transaction_size = transaction_size_map_[host_transaction_id];
    699   if (transaction_size &&
    700       parent_->Context()->WouldBeOverQuota(
    701           transaction_url_map_[host_transaction_id], transaction_size)) {
    702     connection->database()->Abort(
    703         host_transaction_id,
    704         IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError));
    705     return;
    706   }
    707 
    708   connection->database()->Commit(host_transaction_id);
    709 }
    710 
    711 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateIndex(
    712     const IndexedDBHostMsg_DatabaseCreateIndex_Params& params) {
    713   DCHECK(
    714       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    715   IndexedDBConnection* connection =
    716       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
    717   if (!connection)
    718     return;
    719 
    720   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
    721   connection->database()->CreateIndex(host_transaction_id,
    722                                       params.object_store_id,
    723                                       params.index_id,
    724                                       params.name,
    725                                       params.key_path,
    726                                       params.unique,
    727                                       params.multi_entry);
    728   if (parent_->Context()->IsOverQuota(
    729           database_url_map_[params.ipc_database_id])) {
    730     connection->database()->Abort(
    731         host_transaction_id,
    732         IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError));
    733   }
    734 }
    735 
    736 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteIndex(
    737     int32 ipc_database_id,
    738     int64 transaction_id,
    739     int64 object_store_id,
    740     int64 index_id) {
    741   DCHECK(
    742       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    743   IndexedDBConnection* connection =
    744       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
    745   if (!connection)
    746     return;
    747 
    748   connection->database()->DeleteIndex(
    749       parent_->HostTransactionId(transaction_id), object_store_id, index_id);
    750 }
    751 
    752 //////////////////////////////////////////////////////////////////////
    753 // IndexedDBDispatcherHost::CursorDispatcherHost
    754 //
    755 
    756 IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost(
    757     IndexedDBDispatcherHost* parent)
    758     : parent_(parent) {
    759   map_.set_check_on_null_data(true);
    760 }
    761 
    762 IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {}
    763 
    764 bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
    765     const IPC::Message& message,
    766     bool* msg_is_ok) {
    767   DCHECK(
    768       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    769 
    770   bool handled = true;
    771   IPC_BEGIN_MESSAGE_MAP_EX(
    772       IndexedDBDispatcherHost::CursorDispatcherHost, message, *msg_is_ok)
    773     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance)
    774     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
    775     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
    776     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset)
    777     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
    778     IPC_MESSAGE_UNHANDLED(handled = false)
    779   IPC_END_MESSAGE_MAP()
    780   return handled;
    781 }
    782 
    783 void IndexedDBDispatcherHost::CursorDispatcherHost::Send(
    784     IPC::Message* message) {
    785   parent_->Send(message);
    786 }
    787 
    788 void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance(
    789     int32 ipc_cursor_id,
    790     int32 ipc_thread_id,
    791     int32 ipc_callbacks_id,
    792     unsigned long count) {
    793   DCHECK(
    794       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    795   IndexedDBCursor* idb_cursor =
    796       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
    797   if (!idb_cursor)
    798     return;
    799 
    800   idb_cursor->Advance(
    801       count,
    802       new IndexedDBCallbacks(
    803           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
    804 }
    805 
    806 void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
    807     int32 ipc_cursor_id,
    808     int32 ipc_thread_id,
    809     int32 ipc_callbacks_id,
    810     const IndexedDBKey& key) {
    811   DCHECK(
    812       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    813   IndexedDBCursor* idb_cursor =
    814       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
    815   if (!idb_cursor)
    816     return;
    817 
    818   idb_cursor->Continue(
    819       make_scoped_ptr(new IndexedDBKey(key)),
    820       new IndexedDBCallbacks(
    821           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
    822 }
    823 
    824 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
    825     int32 ipc_cursor_id,
    826     int32 ipc_thread_id,
    827     int32 ipc_callbacks_id,
    828     int n) {
    829   DCHECK(
    830       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    831   IndexedDBCursor* idb_cursor =
    832       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
    833   if (!idb_cursor)
    834     return;
    835 
    836   idb_cursor->PrefetchContinue(
    837       n,
    838       new IndexedDBCallbacks(
    839           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
    840 }
    841 
    842 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
    843     int32 ipc_cursor_id,
    844     int used_prefetches,
    845     int unused_prefetches) {
    846   DCHECK(
    847       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    848   IndexedDBCursor* idb_cursor =
    849       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
    850   if (!idb_cursor)
    851     return;
    852 
    853   idb_cursor->PrefetchReset(used_prefetches, unused_prefetches);
    854 }
    855 
    856 void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed(
    857     int32 ipc_object_id) {
    858   DCHECK(
    859       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
    860   parent_->DestroyObject(&map_, ipc_object_id);
    861 }
    862 
    863 }  // namespace content
    864