Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / indexed_db / indexed_db_dispatcher_host.cc
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 "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
30 #include "url/gurl.h"
31 #include "webkit/browser/blob/blob_storage_context.h"
32 #include "webkit/browser/database/database_util.h"
33 #include "webkit/common/database/database_identifier.h"
34
35 using webkit_database::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_);
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_);
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<webkit_blob::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       webkit_database::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::FilePath indexed_db_path = indexed_db_context_->data_path();
295
296   GURL origin_url =
297       webkit_database::GetOriginFromIdentifier(params.database_identifier);
298
299   int64 host_transaction_id = HostTransactionId(params.transaction_id);
300
301   // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
302   // created) if this origin is already over quota.
303   scoped_refptr<IndexedDBCallbacks> callbacks =
304       new IndexedDBCallbacks(this,
305                              params.ipc_thread_id,
306                              params.ipc_callbacks_id,
307                              params.ipc_database_callbacks_id,
308                              host_transaction_id,
309                              origin_url);
310   scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks =
311       new IndexedDBDatabaseCallbacks(
312           this, params.ipc_thread_id, params.ipc_database_callbacks_id);
313   IndexedDBPendingConnection connection(callbacks,
314                                         database_callbacks,
315                                         ipc_process_id_,
316                                         host_transaction_id,
317                                         params.version);
318   DCHECK(request_context_);
319   Context()->GetIDBFactory()->Open(
320       params.name, connection, request_context_, origin_url, indexed_db_path);
321 }
322
323 void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
324     const IndexedDBHostMsg_FactoryDeleteDatabase_Params& params) {
325   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
326   GURL origin_url =
327       webkit_database::GetOriginFromIdentifier(params.database_identifier);
328   base::FilePath indexed_db_path = indexed_db_context_->data_path();
329   DCHECK(request_context_);
330   Context()->GetIDBFactory()->DeleteDatabase(
331       params.name,
332       request_context_,
333       new IndexedDBCallbacks(
334           this, params.ipc_thread_id, params.ipc_callbacks_id),
335       origin_url,
336       indexed_db_path);
337 }
338
339 // OnPutHelper exists only to allow us to hop threads while holding a reference
340 // to the IndexedDBDispatcherHost.
341 void IndexedDBDispatcherHost::OnPutHelper(
342     const IndexedDBHostMsg_DatabasePut_Params& params,
343     std::vector<webkit_blob::BlobDataHandle*> handles) {
344   database_dispatcher_host_->OnPut(params, handles);
345 }
346
347 void IndexedDBDispatcherHost::OnAckReceivedBlobs(
348     const std::vector<std::string>& uuids) {
349   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
350   std::vector<std::string>::const_iterator iter;
351   for (iter = uuids.begin(); iter != uuids.end(); ++iter)
352     DropBlobDataHandle(*iter);
353 }
354
355 void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id,
356                                                 bool committed) {
357   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
358   if (!database_dispatcher_host_)
359     return;
360   TransactionIDToURLMap& transaction_url_map =
361       database_dispatcher_host_->transaction_url_map_;
362   TransactionIDToSizeMap& transaction_size_map =
363       database_dispatcher_host_->transaction_size_map_;
364   TransactionIDToDatabaseIDMap& transaction_database_map =
365       database_dispatcher_host_->transaction_database_map_;
366   if (committed)
367     Context()->TransactionComplete(transaction_url_map[host_transaction_id]);
368   transaction_url_map.erase(host_transaction_id);
369   transaction_size_map.erase(host_transaction_id);
370   transaction_database_map.erase(host_transaction_id);
371 }
372
373 //////////////////////////////////////////////////////////////////////
374 // Helper templates.
375 //
376
377 template <typename ObjectType>
378 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
379     IDMap<ObjectType, IDMapOwnPointer>* map,
380     int32 ipc_return_object_id) {
381   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
382   ObjectType* return_object = map->Lookup(ipc_return_object_id);
383   if (!return_object) {
384     NOTREACHED() << "Uh oh, couldn't find object with id "
385                  << ipc_return_object_id;
386     RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
387     BadMessageReceived();
388   }
389   return return_object;
390 }
391
392 template <typename ObjectType>
393 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
394     RefIDMap<ObjectType>* map,
395     int32 ipc_return_object_id) {
396   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
397   ObjectType* return_object = map->Lookup(ipc_return_object_id);
398   if (!return_object) {
399     NOTREACHED() << "Uh oh, couldn't find object with id "
400                  << ipc_return_object_id;
401     RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
402     BadMessageReceived();
403   }
404   return return_object;
405 }
406
407 template <typename MapType>
408 void IndexedDBDispatcherHost::DestroyObject(MapType* map, int32 ipc_object_id) {
409   GetOrTerminateProcess(map, ipc_object_id);
410   map->Remove(ipc_object_id);
411 }
412
413 //////////////////////////////////////////////////////////////////////
414 // IndexedDBDispatcherHost::DatabaseDispatcherHost
415 //
416
417 IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost(
418     IndexedDBDispatcherHost* parent)
419     : parent_(parent) {
420   map_.set_check_on_null_data(true);
421 }
422
423 IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() {
424   // TODO(alecflett): uncomment these when we find the source of these leaks.
425   // DCHECK(transaction_size_map_.empty());
426   // DCHECK(transaction_url_map_.empty());
427 }
428
429 void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
430   DCHECK(
431       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
432   // Abort outstanding transactions started by connections in the associated
433   // front-end to unblock later transactions. This should only occur on unclean
434   // (crash) or abrupt (process-kill) shutdowns.
435   for (TransactionIDToDatabaseIDMap::iterator iter =
436            transaction_database_map_.begin();
437        iter != transaction_database_map_.end();) {
438     int64 transaction_id = iter->first;
439     int32 ipc_database_id = iter->second;
440     ++iter;
441     IndexedDBConnection* connection = map_.Lookup(ipc_database_id);
442     if (connection && connection->IsConnected()) {
443       connection->database()->Abort(
444           transaction_id,
445           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError));
446     }
447   }
448   DCHECK(transaction_database_map_.empty());
449
450   for (WebIDBObjectIDToURLMap::iterator iter = database_url_map_.begin();
451        iter != database_url_map_.end();
452        iter++) {
453     IndexedDBConnection* connection = map_.Lookup(iter->first);
454     if (connection && connection->IsConnected()) {
455       connection->Close();
456       parent_->Context()->ConnectionClosed(iter->second, connection);
457     }
458   }
459 }
460
461 bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
462     const IPC::Message& message) {
463
464   DCHECK(
465       (message.type() == IndexedDBHostMsg_DatabasePut::ID) ||
466       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
467
468   bool handled = true;
469   IPC_BEGIN_MESSAGE_MAP(
470       IndexedDBDispatcherHost::DatabaseDispatcherHost, message)
471     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore,
472                         OnCreateObjectStore)
473     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore,
474                         OnDeleteObjectStore)
475     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction,
476                         OnCreateTransaction)
477     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose)
478     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseVersionChangeIgnored,
479                         OnVersionChangeIgnored)
480     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed, OnDestroyed)
481     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet, OnGet)
482     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPutWrapper)
483     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys, OnSetIndexKeys)
484     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady,
485                         OnSetIndexesReady)
486     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseOpenCursor, OnOpenCursor)
487     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCount, OnCount)
488     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteRange, OnDeleteRange)
489     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClear, OnClear)
490     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateIndex, OnCreateIndex)
491     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteIndex, OnDeleteIndex)
492     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseAbort, OnAbort)
493     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit, OnCommit)
494     IPC_MESSAGE_UNHANDLED(handled = false)
495   IPC_END_MESSAGE_MAP()
496
497   return handled;
498 }
499
500 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore(
501     const IndexedDBHostMsg_DatabaseCreateObjectStore_Params& params) {
502   DCHECK(
503       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
504   IndexedDBConnection* connection =
505       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
506   if (!connection || !connection->IsConnected())
507     return;
508
509   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
510   connection->database()->CreateObjectStore(host_transaction_id,
511                                             params.object_store_id,
512                                             params.name,
513                                             params.key_path,
514                                             params.auto_increment);
515   if (parent_->Context()->IsOverQuota(
516           database_url_map_[params.ipc_database_id])) {
517     connection->database()->Abort(
518         host_transaction_id,
519         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
520   }
521 }
522
523 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore(
524     int32 ipc_database_id,
525     int64 transaction_id,
526     int64 object_store_id) {
527   DCHECK(
528       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
529   IndexedDBConnection* connection =
530       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
531   if (!connection || !connection->IsConnected())
532     return;
533
534   connection->database()->DeleteObjectStore(
535       parent_->HostTransactionId(transaction_id), object_store_id);
536 }
537
538 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction(
539     const IndexedDBHostMsg_DatabaseCreateTransaction_Params& params) {
540   DCHECK(
541       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
542   IndexedDBConnection* connection =
543       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
544   if (!connection || !connection->IsConnected())
545     return;
546
547   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
548
549   if (transaction_database_map_.find(host_transaction_id) !=
550       transaction_database_map_.end()) {
551     DLOG(ERROR) << "Duplicate host_transaction_id.";
552     return;
553   }
554
555   connection->database()->CreateTransaction(
556       host_transaction_id, connection, params.object_store_ids, params.mode);
557   transaction_database_map_[host_transaction_id] = params.ipc_database_id;
558   parent_->RegisterTransactionId(host_transaction_id,
559                                  database_url_map_[params.ipc_database_id]);
560 }
561
562 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose(
563     int32 ipc_database_id) {
564   DCHECK(
565       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
566   IndexedDBConnection* connection =
567       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
568   if (!connection || !connection->IsConnected())
569     return;
570   connection->Close();
571 }
572
573 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnVersionChangeIgnored(
574     int32 ipc_database_id) {
575   DCHECK(
576       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
577   IndexedDBConnection* connection =
578       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
579   if (!connection || !connection->IsConnected())
580     return;
581   connection->VersionChangeIgnored();
582 }
583
584 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
585     int32 ipc_object_id) {
586   DCHECK(
587       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
588   IndexedDBConnection* connection = map_.Lookup(ipc_object_id);
589   if (connection->IsConnected())
590     connection->Close();
591   parent_->Context()
592       ->ConnectionClosed(database_url_map_[ipc_object_id], connection);
593   database_url_map_.erase(ipc_object_id);
594   parent_->DestroyObject(&map_, ipc_object_id);
595 }
596
597 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet(
598     const IndexedDBHostMsg_DatabaseGet_Params& params) {
599   DCHECK(
600       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
601   IndexedDBConnection* connection =
602       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
603   if (!connection || !connection->IsConnected())
604     return;
605
606   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
607       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
608   connection->database()->Get(
609       parent_->HostTransactionId(params.transaction_id),
610       params.object_store_id,
611       params.index_id,
612       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
613       params.key_only,
614       callbacks);
615 }
616
617 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPutWrapper(
618     const IndexedDBHostMsg_DatabasePut_Params& params) {
619   std::vector<webkit_blob::BlobDataHandle*> handles;
620   for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
621     const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
622     handles.push_back(parent_->blob_storage_context_->context()
623                           ->GetBlobDataFromUUID(info.uuid)
624                           .release());
625   }
626   parent_->indexed_db_context_->TaskRunner()->PostTask(
627       FROM_HERE,
628       base::Bind(
629           &IndexedDBDispatcherHost::OnPutHelper, parent_, params, handles));
630 }
631
632 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
633     const IndexedDBHostMsg_DatabasePut_Params& params,
634     std::vector<webkit_blob::BlobDataHandle*> handles) {
635
636   DCHECK(
637       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
638
639   ScopedVector<webkit_blob::BlobDataHandle> scoped_handles;
640   scoped_handles.swap(handles);
641
642   IndexedDBConnection* connection =
643       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
644   if (!connection || !connection->IsConnected())
645     return;
646   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
647       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
648
649   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
650
651   std::vector<IndexedDBBlobInfo> blob_info(params.blob_or_file_info.size());
652
653   ChildProcessSecurityPolicyImpl* policy =
654       ChildProcessSecurityPolicyImpl::GetInstance();
655
656   for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
657     const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
658     if (info.is_file) {
659       base::FilePath path = base::FilePath::FromUTF16Unsafe(info.file_path);
660       if (!policy->CanReadFile(parent_->ipc_process_id_, path)) {
661         parent_->BadMessageReceived();
662         return;
663       }
664       blob_info[i] =
665           IndexedDBBlobInfo(info.uuid, path, info.file_name, info.mime_type);
666       if (info.size != static_cast<uint64_t>(-1)) {
667         blob_info[i].set_last_modified(
668             base::Time::FromDoubleT(info.last_modified));
669         blob_info[i].set_size(info.size);
670       }
671     } else {
672       blob_info[i] = IndexedDBBlobInfo(info.uuid, info.mime_type, info.size);
673     }
674   }
675
676   // TODO(alecflett): Avoid a copy here.
677   IndexedDBValue value;
678   value.bits = params.value;
679   value.blob_info.swap(blob_info);
680   connection->database()->Put(host_transaction_id,
681                               params.object_store_id,
682                               &value,
683                               &scoped_handles,
684                               make_scoped_ptr(new IndexedDBKey(params.key)),
685                               params.put_mode,
686                               callbacks,
687                               params.index_keys);
688   TransactionIDToSizeMap* map =
689       &parent_->database_dispatcher_host_->transaction_size_map_;
690   // Size can't be big enough to overflow because it represents the
691   // actual bytes passed through IPC.
692   (*map)[host_transaction_id] += params.value.size();
693 }
694
695 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys(
696     const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params) {
697   DCHECK(
698       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
699   IndexedDBConnection* connection =
700       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
701   if (!connection || !connection->IsConnected())
702     return;
703
704   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
705   connection->database()->SetIndexKeys(
706       host_transaction_id,
707       params.object_store_id,
708       make_scoped_ptr(new IndexedDBKey(params.primary_key)),
709       params.index_keys);
710 }
711
712 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady(
713     int32 ipc_database_id,
714     int64 transaction_id,
715     int64 object_store_id,
716     const std::vector<int64>& index_ids) {
717   DCHECK(
718       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
719   IndexedDBConnection* connection =
720       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
721   if (!connection || !connection->IsConnected())
722     return;
723
724   connection->database()->SetIndexesReady(
725       parent_->HostTransactionId(transaction_id), object_store_id, index_ids);
726 }
727
728 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpenCursor(
729     const IndexedDBHostMsg_DatabaseOpenCursor_Params& params) {
730   DCHECK(
731       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
732   IndexedDBConnection* connection =
733       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
734   if (!connection || !connection->IsConnected())
735     return;
736
737   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
738       parent_, params.ipc_thread_id, params.ipc_callbacks_id, -1));
739   connection->database()->OpenCursor(
740       parent_->HostTransactionId(params.transaction_id),
741       params.object_store_id,
742       params.index_id,
743       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
744       params.direction,
745       params.key_only,
746       params.task_type,
747       callbacks);
748 }
749
750 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCount(
751     const IndexedDBHostMsg_DatabaseCount_Params& params) {
752   DCHECK(
753       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
754   IndexedDBConnection* connection =
755       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
756   if (!connection || !connection->IsConnected())
757     return;
758
759   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
760       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
761   connection->database()->Count(
762       parent_->HostTransactionId(params.transaction_id),
763       params.object_store_id,
764       params.index_id,
765       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
766       callbacks);
767 }
768
769 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteRange(
770     const IndexedDBHostMsg_DatabaseDeleteRange_Params& params) {
771   DCHECK(
772       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
773   IndexedDBConnection* connection =
774       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
775   if (!connection || !connection->IsConnected())
776     return;
777
778   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
779       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
780   connection->database()->DeleteRange(
781       parent_->HostTransactionId(params.transaction_id),
782       params.object_store_id,
783       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
784       callbacks);
785 }
786
787 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear(
788     int32 ipc_thread_id,
789     int32 ipc_callbacks_id,
790     int32 ipc_database_id,
791     int64 transaction_id,
792     int64 object_store_id) {
793   DCHECK(
794       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
795   IndexedDBConnection* connection =
796       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
797   if (!connection || !connection->IsConnected())
798     return;
799
800   scoped_refptr<IndexedDBCallbacks> callbacks(
801       new IndexedDBCallbacks(parent_, ipc_thread_id, ipc_callbacks_id));
802
803   connection->database()->Clear(
804       parent_->HostTransactionId(transaction_id), object_store_id, callbacks);
805 }
806
807 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnAbort(
808     int32 ipc_database_id,
809     int64 transaction_id) {
810   DCHECK(
811       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
812   IndexedDBConnection* connection =
813       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
814   if (!connection || !connection->IsConnected())
815     return;
816
817   connection->database()->Abort(parent_->HostTransactionId(transaction_id));
818 }
819
820 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit(
821     int32 ipc_database_id,
822     int64 transaction_id) {
823   DCHECK(
824       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
825   IndexedDBConnection* connection =
826       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
827   if (!connection || !connection->IsConnected())
828     return;
829
830   int64 host_transaction_id = parent_->HostTransactionId(transaction_id);
831   int64 transaction_size = transaction_size_map_[host_transaction_id];
832   if (transaction_size &&
833       parent_->Context()->WouldBeOverQuota(
834           transaction_url_map_[host_transaction_id], transaction_size)) {
835     connection->database()->Abort(
836         host_transaction_id,
837         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
838     return;
839   }
840
841   connection->database()->Commit(host_transaction_id);
842 }
843
844 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateIndex(
845     const IndexedDBHostMsg_DatabaseCreateIndex_Params& params) {
846   DCHECK(
847       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
848   IndexedDBConnection* connection =
849       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
850   if (!connection || !connection->IsConnected())
851     return;
852
853   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
854   connection->database()->CreateIndex(host_transaction_id,
855                                       params.object_store_id,
856                                       params.index_id,
857                                       params.name,
858                                       params.key_path,
859                                       params.unique,
860                                       params.multi_entry);
861   if (parent_->Context()->IsOverQuota(
862           database_url_map_[params.ipc_database_id])) {
863     connection->database()->Abort(
864         host_transaction_id,
865         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
866   }
867 }
868
869 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteIndex(
870     int32 ipc_database_id,
871     int64 transaction_id,
872     int64 object_store_id,
873     int64 index_id) {
874   DCHECK(
875       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
876   IndexedDBConnection* connection =
877       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
878   if (!connection || !connection->IsConnected())
879     return;
880
881   connection->database()->DeleteIndex(
882       parent_->HostTransactionId(transaction_id), object_store_id, index_id);
883 }
884
885 //////////////////////////////////////////////////////////////////////
886 // IndexedDBDispatcherHost::CursorDispatcherHost
887 //
888
889 IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost(
890     IndexedDBDispatcherHost* parent)
891     : parent_(parent) {
892   map_.set_check_on_null_data(true);
893 }
894
895 IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {}
896
897 bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
898     const IPC::Message& message) {
899   bool handled = true;
900   IPC_BEGIN_MESSAGE_MAP(
901       IndexedDBDispatcherHost::CursorDispatcherHost, message)
902     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance)
903     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
904     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
905     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset)
906     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
907     IPC_MESSAGE_UNHANDLED(handled = false)
908   IPC_END_MESSAGE_MAP()
909
910   DCHECK(
911       !handled ||
912       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
913
914   return handled;
915 }
916
917 void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance(
918     int32 ipc_cursor_id,
919     int32 ipc_thread_id,
920     int32 ipc_callbacks_id,
921     uint32 count) {
922   DCHECK(
923       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
924   IndexedDBCursor* idb_cursor =
925       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
926   if (!idb_cursor)
927     return;
928
929   idb_cursor->Advance(
930       count,
931       new IndexedDBCallbacks(
932           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
933 }
934
935 void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
936     int32 ipc_cursor_id,
937     int32 ipc_thread_id,
938     int32 ipc_callbacks_id,
939     const IndexedDBKey& key,
940     const IndexedDBKey& primary_key) {
941   DCHECK(
942       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
943   IndexedDBCursor* idb_cursor =
944       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
945   if (!idb_cursor)
946     return;
947
948   idb_cursor->Continue(
949       key.IsValid() ? make_scoped_ptr(new IndexedDBKey(key))
950                     : scoped_ptr<IndexedDBKey>(),
951       primary_key.IsValid() ? make_scoped_ptr(new IndexedDBKey(primary_key))
952                             : scoped_ptr<IndexedDBKey>(),
953       new IndexedDBCallbacks(
954           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
955 }
956
957 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
958     int32 ipc_cursor_id,
959     int32 ipc_thread_id,
960     int32 ipc_callbacks_id,
961     int n) {
962   DCHECK(
963       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
964   IndexedDBCursor* idb_cursor =
965       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
966   if (!idb_cursor)
967     return;
968
969   idb_cursor->PrefetchContinue(
970       n,
971       new IndexedDBCallbacks(
972           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
973 }
974
975 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
976     int32 ipc_cursor_id,
977     int used_prefetches,
978     int unused_prefetches) {
979   DCHECK(
980       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
981   IndexedDBCursor* idb_cursor =
982       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
983   if (!idb_cursor)
984     return;
985
986   leveldb::Status s =
987       idb_cursor->PrefetchReset(used_prefetches, unused_prefetches);
988   // TODO(cmumford): Handle this error (crbug.com/363397)
989   if (!s.ok())
990     DLOG(ERROR) << "Unable to reset prefetch";
991 }
992
993 void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed(
994     int32 ipc_object_id) {
995   DCHECK(
996       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
997   parent_->DestroyObject(&map_, ipc_object_id);
998 }
999
1000 }  // namespace content