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