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