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