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