4f49b0e190ae31ab029cdb1484dda1ca007e7363
[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                                                 bool* message_was_ok) {
129   if (IPC_MESSAGE_CLASS(message) != IndexedDBMsgStart)
130     return false;
131
132   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread() ||
133          message.type() == IndexedDBHostMsg_DatabasePut::ID);
134
135   bool handled =
136       database_dispatcher_host_->OnMessageReceived(message, message_was_ok) ||
137       cursor_dispatcher_host_->OnMessageReceived(message, message_was_ok);
138
139   if (!handled) {
140     handled = true;
141     IPC_BEGIN_MESSAGE_MAP_EX(IndexedDBDispatcherHost, message, *message_was_ok)
142       IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryGetDatabaseNames,
143                           OnIDBFactoryGetDatabaseNames)
144       IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryOpen, OnIDBFactoryOpen)
145       IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryDeleteDatabase,
146                           OnIDBFactoryDeleteDatabase)
147       IPC_MESSAGE_HANDLER(IndexedDBHostMsg_AckReceivedBlobs, OnAckReceivedBlobs)
148       IPC_MESSAGE_UNHANDLED(handled = false)
149     IPC_END_MESSAGE_MAP()
150   }
151   return handled;
152 }
153
154 int32 IndexedDBDispatcherHost::Add(IndexedDBCursor* cursor) {
155   if (!cursor_dispatcher_host_) {
156     return 0;
157   }
158   return cursor_dispatcher_host_->map_.Add(cursor);
159 }
160
161 int32 IndexedDBDispatcherHost::Add(IndexedDBConnection* connection,
162                                    int32 ipc_thread_id,
163                                    const GURL& origin_url) {
164   if (!database_dispatcher_host_) {
165     connection->Close();
166     delete connection;
167     return -1;
168   }
169   int32 ipc_database_id = database_dispatcher_host_->map_.Add(connection);
170   Context()->ConnectionOpened(origin_url, connection);
171   database_dispatcher_host_->database_url_map_[ipc_database_id] = origin_url;
172   return ipc_database_id;
173 }
174
175 void IndexedDBDispatcherHost::RegisterTransactionId(int64 host_transaction_id,
176                                                     const GURL& url) {
177   if (!database_dispatcher_host_)
178     return;
179   database_dispatcher_host_->transaction_url_map_[host_transaction_id] = url;
180 }
181
182 int64 IndexedDBDispatcherHost::HostTransactionId(int64 transaction_id) {
183   // Inject the renderer process id into the transaction id, to
184   // uniquely identify this transaction, and effectively bind it to
185   // the renderer that initiated it. The lower 32 bits of
186   // transaction_id are guaranteed to be unique within that renderer.
187   base::ProcessId pid = peer_pid();
188   DCHECK(!(transaction_id >> 32)) << "Transaction ids can only be 32 bits";
189   COMPILE_ASSERT(sizeof(base::ProcessId) <= sizeof(int32),
190                  Process_ID_must_fit_in_32_bits);
191
192   return transaction_id | (static_cast<uint64>(pid) << 32);
193 }
194
195 int64 IndexedDBDispatcherHost::RendererTransactionId(
196     int64 host_transaction_id) {
197   DCHECK(host_transaction_id >> 32 == peer_pid())
198       << "Invalid renderer target for transaction id";
199   return host_transaction_id & 0xffffffff;
200 }
201
202 // static
203 uint32 IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
204     int64 host_transaction_id) {
205   return host_transaction_id & 0xffffffff;
206 }
207
208 // static
209 uint32 IndexedDBDispatcherHost::TransactionIdToProcessId(
210     int64 host_transaction_id) {
211   return (host_transaction_id >> 32) & 0xffffffff;
212 }
213
214 void IndexedDBDispatcherHost::HoldBlobDataHandle(
215     const std::string& uuid,
216     scoped_ptr<webkit_blob::BlobDataHandle>& blob_data_handle) {
217   DCHECK(!ContainsKey(blob_data_handle_map_, uuid));
218   blob_data_handle_map_[uuid] = blob_data_handle.release();
219 }
220
221 void IndexedDBDispatcherHost::DropBlobDataHandle(const std::string& uuid) {
222   BlobDataHandleMap::iterator iter = blob_data_handle_map_.find(uuid);
223   if (iter != blob_data_handle_map_.end()) {
224     delete iter->second;
225     blob_data_handle_map_.erase(iter);
226   } else {
227     DLOG(FATAL) << "Failed to find blob UUID in map:" << uuid;
228   }
229 }
230
231 IndexedDBCursor* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id) {
232   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
233   return cursor_dispatcher_host_->map_.Lookup(ipc_cursor_id);
234 }
235
236 ::IndexedDBDatabaseMetadata IndexedDBDispatcherHost::ConvertMetadata(
237     const content::IndexedDBDatabaseMetadata& web_metadata) {
238   ::IndexedDBDatabaseMetadata metadata;
239   metadata.id = web_metadata.id;
240   metadata.name = web_metadata.name;
241   metadata.version = web_metadata.version;
242   metadata.int_version = web_metadata.int_version;
243   metadata.max_object_store_id = web_metadata.max_object_store_id;
244
245   for (content::IndexedDBDatabaseMetadata::ObjectStoreMap::const_iterator iter =
246            web_metadata.object_stores.begin();
247        iter != web_metadata.object_stores.end();
248        ++iter) {
249
250     const content::IndexedDBObjectStoreMetadata& web_store_metadata =
251         iter->second;
252     ::IndexedDBObjectStoreMetadata idb_store_metadata;
253     idb_store_metadata.id = web_store_metadata.id;
254     idb_store_metadata.name = web_store_metadata.name;
255     idb_store_metadata.keyPath = web_store_metadata.key_path;
256     idb_store_metadata.autoIncrement = web_store_metadata.auto_increment;
257     idb_store_metadata.max_index_id = web_store_metadata.max_index_id;
258
259     for (content::IndexedDBObjectStoreMetadata::IndexMap::const_iterator
260              index_iter = web_store_metadata.indexes.begin();
261          index_iter != web_store_metadata.indexes.end();
262          ++index_iter) {
263       const content::IndexedDBIndexMetadata& web_index_metadata =
264           index_iter->second;
265       ::IndexedDBIndexMetadata idb_index_metadata;
266       idb_index_metadata.id = web_index_metadata.id;
267       idb_index_metadata.name = web_index_metadata.name;
268       idb_index_metadata.keyPath = web_index_metadata.key_path;
269       idb_index_metadata.unique = web_index_metadata.unique;
270       idb_index_metadata.multiEntry = web_index_metadata.multi_entry;
271       idb_store_metadata.indexes.push_back(idb_index_metadata);
272     }
273     metadata.object_stores.push_back(idb_store_metadata);
274   }
275   return metadata;
276 }
277
278 void IndexedDBDispatcherHost::OnIDBFactoryGetDatabaseNames(
279     const IndexedDBHostMsg_FactoryGetDatabaseNames_Params& params) {
280   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
281   base::FilePath indexed_db_path = indexed_db_context_->data_path();
282
283   GURL origin_url =
284       webkit_database::GetOriginFromIdentifier(params.database_identifier);
285
286   Context()->GetIDBFactory()->GetDatabaseNames(
287       new IndexedDBCallbacks(
288           this, params.ipc_thread_id, params.ipc_callbacks_id),
289       origin_url,
290       indexed_db_path,
291       request_context_);
292 }
293
294 void IndexedDBDispatcherHost::OnIDBFactoryOpen(
295     const IndexedDBHostMsg_FactoryOpen_Params& params) {
296   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
297   base::FilePath indexed_db_path = indexed_db_context_->data_path();
298
299   GURL origin_url =
300       webkit_database::GetOriginFromIdentifier(params.database_identifier);
301
302   int64 host_transaction_id = HostTransactionId(params.transaction_id);
303
304   // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
305   // created) if this origin is already over quota.
306   scoped_refptr<IndexedDBCallbacks> callbacks =
307       new IndexedDBCallbacks(this,
308                              params.ipc_thread_id,
309                              params.ipc_callbacks_id,
310                              params.ipc_database_callbacks_id,
311                              host_transaction_id,
312                              origin_url);
313   scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks =
314       new IndexedDBDatabaseCallbacks(
315           this, params.ipc_thread_id, params.ipc_database_callbacks_id);
316   IndexedDBPendingConnection connection(callbacks,
317                                         database_callbacks,
318                                         ipc_process_id_,
319                                         host_transaction_id,
320                                         params.version);
321   DCHECK(request_context_);
322   Context()->GetIDBFactory()->Open(
323       params.name, connection, request_context_, origin_url, indexed_db_path);
324 }
325
326 void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
327     const IndexedDBHostMsg_FactoryDeleteDatabase_Params& params) {
328   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
329   GURL origin_url =
330       webkit_database::GetOriginFromIdentifier(params.database_identifier);
331   base::FilePath indexed_db_path = indexed_db_context_->data_path();
332   DCHECK(request_context_);
333   Context()->GetIDBFactory()->DeleteDatabase(
334       params.name,
335       request_context_,
336       new IndexedDBCallbacks(
337           this, params.ipc_thread_id, params.ipc_callbacks_id),
338       origin_url,
339       indexed_db_path);
340 }
341
342 // OnPutHelper exists only to allow us to hop threads while holding a reference
343 // to the IndexedDBDispatcherHost.
344 void IndexedDBDispatcherHost::OnPutHelper(
345     const IndexedDBHostMsg_DatabasePut_Params& params,
346     std::vector<webkit_blob::BlobDataHandle*> handles) {
347   database_dispatcher_host_->OnPut(params, handles);
348 }
349
350 void IndexedDBDispatcherHost::OnAckReceivedBlobs(
351     const std::vector<std::string>& uuids) {
352   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
353   std::vector<std::string>::const_iterator iter;
354   for (iter = uuids.begin(); iter != uuids.end(); ++iter)
355     DropBlobDataHandle(*iter);
356 }
357
358 void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id,
359                                                 bool committed) {
360   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
361   if (!database_dispatcher_host_)
362     return;
363   TransactionIDToURLMap& transaction_url_map =
364       database_dispatcher_host_->transaction_url_map_;
365   TransactionIDToSizeMap& transaction_size_map =
366       database_dispatcher_host_->transaction_size_map_;
367   TransactionIDToDatabaseIDMap& transaction_database_map =
368       database_dispatcher_host_->transaction_database_map_;
369   if (committed)
370     Context()->TransactionComplete(transaction_url_map[host_transaction_id]);
371   transaction_url_map.erase(host_transaction_id);
372   transaction_size_map.erase(host_transaction_id);
373   transaction_database_map.erase(host_transaction_id);
374 }
375
376 //////////////////////////////////////////////////////////////////////
377 // Helper templates.
378 //
379
380 template <typename ObjectType>
381 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
382     IDMap<ObjectType, IDMapOwnPointer>* map,
383     int32 ipc_return_object_id) {
384   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
385   ObjectType* return_object = map->Lookup(ipc_return_object_id);
386   if (!return_object) {
387     NOTREACHED() << "Uh oh, couldn't find object with id "
388                  << ipc_return_object_id;
389     RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
390     BadMessageReceived();
391   }
392   return return_object;
393 }
394
395 template <typename ObjectType>
396 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
397     RefIDMap<ObjectType>* map,
398     int32 ipc_return_object_id) {
399   DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
400   ObjectType* return_object = map->Lookup(ipc_return_object_id);
401   if (!return_object) {
402     NOTREACHED() << "Uh oh, couldn't find object with id "
403                  << ipc_return_object_id;
404     RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
405     BadMessageReceived();
406   }
407   return return_object;
408 }
409
410 template <typename MapType>
411 void IndexedDBDispatcherHost::DestroyObject(MapType* map, int32 ipc_object_id) {
412   GetOrTerminateProcess(map, ipc_object_id);
413   map->Remove(ipc_object_id);
414 }
415
416 //////////////////////////////////////////////////////////////////////
417 // IndexedDBDispatcherHost::DatabaseDispatcherHost
418 //
419
420 IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost(
421     IndexedDBDispatcherHost* parent)
422     : parent_(parent) {
423   map_.set_check_on_null_data(true);
424 }
425
426 IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() {
427   // TODO(alecflett): uncomment these when we find the source of these leaks.
428   // DCHECK(transaction_size_map_.empty());
429   // DCHECK(transaction_url_map_.empty());
430 }
431
432 void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
433   DCHECK(
434       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
435   // Abort outstanding transactions started by connections in the associated
436   // front-end to unblock later transactions. This should only occur on unclean
437   // (crash) or abrupt (process-kill) shutdowns.
438   for (TransactionIDToDatabaseIDMap::iterator iter =
439            transaction_database_map_.begin();
440        iter != transaction_database_map_.end();) {
441     int64 transaction_id = iter->first;
442     int32 ipc_database_id = iter->second;
443     ++iter;
444     IndexedDBConnection* connection = map_.Lookup(ipc_database_id);
445     if (connection && connection->IsConnected()) {
446       connection->database()->Abort(
447           transaction_id,
448           IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError));
449     }
450   }
451   DCHECK(transaction_database_map_.empty());
452
453   for (WebIDBObjectIDToURLMap::iterator iter = database_url_map_.begin();
454        iter != database_url_map_.end();
455        iter++) {
456     IndexedDBConnection* connection = map_.Lookup(iter->first);
457     if (connection && connection->IsConnected()) {
458       connection->Close();
459       parent_->Context()->ConnectionClosed(iter->second, connection);
460     }
461   }
462 }
463
464 bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
465     const IPC::Message& message,
466     bool* msg_is_ok) {
467
468   DCHECK(
469       (message.type() == IndexedDBHostMsg_DatabasePut::ID) ||
470       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
471
472   bool handled = true;
473   IPC_BEGIN_MESSAGE_MAP_EX(
474       IndexedDBDispatcherHost::DatabaseDispatcherHost, message, *msg_is_ok)
475     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore,
476                         OnCreateObjectStore)
477     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore,
478                         OnDeleteObjectStore)
479     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction,
480                         OnCreateTransaction)
481     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose)
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::OnDestroyed(
576     int32 ipc_object_id) {
577   DCHECK(
578       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
579   IndexedDBConnection* connection = map_.Lookup(ipc_object_id);
580   if (connection->IsConnected())
581     connection->Close();
582   parent_->Context()
583       ->ConnectionClosed(database_url_map_[ipc_object_id], connection);
584   database_url_map_.erase(ipc_object_id);
585   parent_->DestroyObject(&map_, ipc_object_id);
586 }
587
588 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet(
589     const IndexedDBHostMsg_DatabaseGet_Params& params) {
590   DCHECK(
591       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
592   IndexedDBConnection* connection =
593       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
594   if (!connection || !connection->IsConnected())
595     return;
596
597   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
598       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
599   connection->database()->Get(
600       parent_->HostTransactionId(params.transaction_id),
601       params.object_store_id,
602       params.index_id,
603       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
604       params.key_only,
605       callbacks);
606 }
607
608 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPutWrapper(
609     const IndexedDBHostMsg_DatabasePut_Params& params) {
610   std::vector<webkit_blob::BlobDataHandle*> handles;
611   for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
612     const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
613     handles.push_back(parent_->blob_storage_context_->context()
614                           ->GetBlobDataFromUUID(info.uuid)
615                           .release());
616   }
617   parent_->indexed_db_context_->TaskRunner()->PostTask(
618       FROM_HERE,
619       base::Bind(
620           &IndexedDBDispatcherHost::OnPutHelper, parent_, params, handles));
621 }
622
623 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
624     const IndexedDBHostMsg_DatabasePut_Params& params,
625     std::vector<webkit_blob::BlobDataHandle*> handles) {
626
627   DCHECK(
628       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
629
630   ScopedVector<webkit_blob::BlobDataHandle> scoped_handles;
631   scoped_handles.swap(handles);
632
633   IndexedDBConnection* connection =
634       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
635   if (!connection || !connection->IsConnected())
636     return;
637   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
638       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
639
640   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
641
642   std::vector<IndexedDBBlobInfo> blob_info(params.blob_or_file_info.size());
643
644   ChildProcessSecurityPolicyImpl* policy =
645       ChildProcessSecurityPolicyImpl::GetInstance();
646
647   for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
648     const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
649     if (info.is_file) {
650       base::FilePath path = base::FilePath::FromUTF16Unsafe(info.file_path);
651       if (!policy->CanReadFile(parent_->ipc_process_id_, path)) {
652         parent_->BadMessageReceived();
653         return;
654       }
655       blob_info[i] =
656           IndexedDBBlobInfo(info.uuid, path, info.file_name, info.mime_type);
657     } else {
658       blob_info[i] = IndexedDBBlobInfo(info.uuid, info.mime_type, info.size);
659     }
660   }
661
662   // TODO(alecflett): Avoid a copy here.
663   IndexedDBValue value;
664   value.bits = params.value;
665   value.blob_info.swap(blob_info);
666   connection->database()->Put(
667       host_transaction_id,
668       params.object_store_id,
669       &value,
670       &scoped_handles,
671       make_scoped_ptr(new IndexedDBKey(params.key)),
672       static_cast<IndexedDBDatabase::PutMode>(params.put_mode),
673       callbacks,
674       params.index_keys);
675   TransactionIDToSizeMap* map =
676       &parent_->database_dispatcher_host_->transaction_size_map_;
677   // Size can't be big enough to overflow because it represents the
678   // actual bytes passed through IPC.
679   (*map)[host_transaction_id] += params.value.size();
680 }
681
682 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys(
683     const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params) {
684   DCHECK(
685       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
686   IndexedDBConnection* connection =
687       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
688   if (!connection || !connection->IsConnected())
689     return;
690
691   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
692   connection->database()->SetIndexKeys(
693       host_transaction_id,
694       params.object_store_id,
695       make_scoped_ptr(new IndexedDBKey(params.primary_key)),
696       params.index_keys);
697 }
698
699 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady(
700     int32 ipc_database_id,
701     int64 transaction_id,
702     int64 object_store_id,
703     const std::vector<int64>& index_ids) {
704   DCHECK(
705       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
706   IndexedDBConnection* connection =
707       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
708   if (!connection || !connection->IsConnected())
709     return;
710
711   connection->database()->SetIndexesReady(
712       parent_->HostTransactionId(transaction_id), object_store_id, index_ids);
713 }
714
715 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpenCursor(
716     const IndexedDBHostMsg_DatabaseOpenCursor_Params& params) {
717   DCHECK(
718       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
719   IndexedDBConnection* connection =
720       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
721   if (!connection || !connection->IsConnected())
722     return;
723
724   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
725       parent_, params.ipc_thread_id, params.ipc_callbacks_id, -1));
726   connection->database()->OpenCursor(
727       parent_->HostTransactionId(params.transaction_id),
728       params.object_store_id,
729       params.index_id,
730       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
731       static_cast<indexed_db::CursorDirection>(params.direction),
732       params.key_only,
733       static_cast<IndexedDBDatabase::TaskType>(params.task_type),
734       callbacks);
735 }
736
737 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCount(
738     const IndexedDBHostMsg_DatabaseCount_Params& params) {
739   DCHECK(
740       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
741   IndexedDBConnection* connection =
742       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
743   if (!connection || !connection->IsConnected())
744     return;
745
746   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
747       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
748   connection->database()->Count(
749       parent_->HostTransactionId(params.transaction_id),
750       params.object_store_id,
751       params.index_id,
752       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
753       callbacks);
754 }
755
756 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteRange(
757     const IndexedDBHostMsg_DatabaseDeleteRange_Params& params) {
758   DCHECK(
759       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
760   IndexedDBConnection* connection =
761       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
762   if (!connection || !connection->IsConnected())
763     return;
764
765   scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
766       parent_, params.ipc_thread_id, params.ipc_callbacks_id));
767   connection->database()->DeleteRange(
768       parent_->HostTransactionId(params.transaction_id),
769       params.object_store_id,
770       make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
771       callbacks);
772 }
773
774 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear(
775     int32 ipc_thread_id,
776     int32 ipc_callbacks_id,
777     int32 ipc_database_id,
778     int64 transaction_id,
779     int64 object_store_id) {
780   DCHECK(
781       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
782   IndexedDBConnection* connection =
783       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
784   if (!connection || !connection->IsConnected())
785     return;
786
787   scoped_refptr<IndexedDBCallbacks> callbacks(
788       new IndexedDBCallbacks(parent_, ipc_thread_id, ipc_callbacks_id));
789
790   connection->database()->Clear(
791       parent_->HostTransactionId(transaction_id), object_store_id, callbacks);
792 }
793
794 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnAbort(
795     int32 ipc_database_id,
796     int64 transaction_id) {
797   DCHECK(
798       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
799   IndexedDBConnection* connection =
800       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
801   if (!connection || !connection->IsConnected())
802     return;
803
804   connection->database()->Abort(parent_->HostTransactionId(transaction_id));
805 }
806
807 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit(
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   int64 host_transaction_id = parent_->HostTransactionId(transaction_id);
818   int64 transaction_size = transaction_size_map_[host_transaction_id];
819   if (transaction_size &&
820       parent_->Context()->WouldBeOverQuota(
821           transaction_url_map_[host_transaction_id], transaction_size)) {
822     connection->database()->Abort(
823         host_transaction_id,
824         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
825     return;
826   }
827
828   connection->database()->Commit(host_transaction_id);
829 }
830
831 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateIndex(
832     const IndexedDBHostMsg_DatabaseCreateIndex_Params& params) {
833   DCHECK(
834       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
835   IndexedDBConnection* connection =
836       parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
837   if (!connection || !connection->IsConnected())
838     return;
839
840   int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
841   connection->database()->CreateIndex(host_transaction_id,
842                                       params.object_store_id,
843                                       params.index_id,
844                                       params.name,
845                                       params.key_path,
846                                       params.unique,
847                                       params.multi_entry);
848   if (parent_->Context()->IsOverQuota(
849           database_url_map_[params.ipc_database_id])) {
850     connection->database()->Abort(
851         host_transaction_id,
852         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
853   }
854 }
855
856 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteIndex(
857     int32 ipc_database_id,
858     int64 transaction_id,
859     int64 object_store_id,
860     int64 index_id) {
861   DCHECK(
862       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
863   IndexedDBConnection* connection =
864       parent_->GetOrTerminateProcess(&map_, ipc_database_id);
865   if (!connection || !connection->IsConnected())
866     return;
867
868   connection->database()->DeleteIndex(
869       parent_->HostTransactionId(transaction_id), object_store_id, index_id);
870 }
871
872 //////////////////////////////////////////////////////////////////////
873 // IndexedDBDispatcherHost::CursorDispatcherHost
874 //
875
876 IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost(
877     IndexedDBDispatcherHost* parent)
878     : parent_(parent) {
879   map_.set_check_on_null_data(true);
880 }
881
882 IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {}
883
884 bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
885     const IPC::Message& message,
886     bool* msg_is_ok) {
887   bool handled = true;
888   IPC_BEGIN_MESSAGE_MAP_EX(
889       IndexedDBDispatcherHost::CursorDispatcherHost, message, *msg_is_ok)
890     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance)
891     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
892     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
893     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset)
894     IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
895     IPC_MESSAGE_UNHANDLED(handled = false)
896   IPC_END_MESSAGE_MAP()
897
898   DCHECK(
899       !handled ||
900       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
901
902   return handled;
903 }
904
905 void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance(
906     int32 ipc_cursor_id,
907     int32 ipc_thread_id,
908     int32 ipc_callbacks_id,
909     unsigned long count) {
910   DCHECK(
911       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
912   IndexedDBCursor* idb_cursor =
913       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
914   if (!idb_cursor)
915     return;
916
917   idb_cursor->Advance(
918       count,
919       new IndexedDBCallbacks(
920           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
921 }
922
923 void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
924     int32 ipc_cursor_id,
925     int32 ipc_thread_id,
926     int32 ipc_callbacks_id,
927     const IndexedDBKey& key,
928     const IndexedDBKey& primary_key) {
929   DCHECK(
930       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
931   IndexedDBCursor* idb_cursor =
932       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
933   if (!idb_cursor)
934     return;
935
936   idb_cursor->Continue(
937       key.IsValid() ? make_scoped_ptr(new IndexedDBKey(key))
938                     : scoped_ptr<IndexedDBKey>(),
939       primary_key.IsValid() ? make_scoped_ptr(new IndexedDBKey(primary_key))
940                             : scoped_ptr<IndexedDBKey>(),
941       new IndexedDBCallbacks(
942           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
943 }
944
945 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
946     int32 ipc_cursor_id,
947     int32 ipc_thread_id,
948     int32 ipc_callbacks_id,
949     int n) {
950   DCHECK(
951       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
952   IndexedDBCursor* idb_cursor =
953       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
954   if (!idb_cursor)
955     return;
956
957   idb_cursor->PrefetchContinue(
958       n,
959       new IndexedDBCallbacks(
960           parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
961 }
962
963 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
964     int32 ipc_cursor_id,
965     int used_prefetches,
966     int unused_prefetches) {
967   DCHECK(
968       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
969   IndexedDBCursor* idb_cursor =
970       parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
971   if (!idb_cursor)
972     return;
973
974   leveldb::Status s =
975       idb_cursor->PrefetchReset(used_prefetches, unused_prefetches);
976   // TODO(cmumford): Handle this error (crbug.com/363397)
977   if (!s.ok())
978     DLOG(ERROR) << "Unable to reset prefetch";
979 }
980
981 void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed(
982     int32 ipc_object_id) {
983   DCHECK(
984       parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
985   parent_->DestroyObject(&map_, ipc_object_id);
986 }
987
988 }  // namespace content