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