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.
5 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/guid.h"
11 #include "base/memory/scoped_vector.h"
12 #include "base/process/process.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/browser/bad_message.h"
16 #include "content/browser/child_process_security_policy_impl.h"
17 #include "content/browser/indexed_db/indexed_db_callbacks.h"
18 #include "content/browser/indexed_db/indexed_db_connection.h"
19 #include "content/browser/indexed_db/indexed_db_context_impl.h"
20 #include "content/browser/indexed_db/indexed_db_cursor.h"
21 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
22 #include "content/browser/indexed_db/indexed_db_metadata.h"
23 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
24 #include "content/browser/indexed_db/indexed_db_value.h"
25 #include "content/browser/renderer_host/render_message_filter.h"
26 #include "content/common/indexed_db/indexed_db_messages.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/user_metrics.h"
29 #include "content/public/common/content_switches.h"
30 #include "content/public/common/result_codes.h"
31 #include "storage/browser/blob/blob_data_builder.h"
32 #include "storage/browser/blob/blob_storage_context.h"
33 #include "storage/browser/database/database_util.h"
34 #include "storage/common/database/database_identifier.h"
35 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
38 using storage::DatabaseUtil
;
39 using blink::WebIDBKey
;
43 IndexedDBDispatcherHost::IndexedDBDispatcherHost(
45 net::URLRequestContextGetter
* request_context_getter
,
46 IndexedDBContextImpl
* indexed_db_context
,
47 ChromeBlobStorageContext
* blob_storage_context
)
48 : BrowserMessageFilter(IndexedDBMsgStart
),
49 request_context_getter_(request_context_getter
),
50 request_context_(NULL
),
51 indexed_db_context_(indexed_db_context
),
52 blob_storage_context_(blob_storage_context
),
53 database_dispatcher_host_(new DatabaseDispatcherHost(this)),
54 cursor_dispatcher_host_(new CursorDispatcherHost(this)),
55 ipc_process_id_(ipc_process_id
) {
56 DCHECK(indexed_db_context_
.get());
59 IndexedDBDispatcherHost::IndexedDBDispatcherHost(
61 net::URLRequestContext
* request_context
,
62 IndexedDBContextImpl
* indexed_db_context
,
63 ChromeBlobStorageContext
* blob_storage_context
)
64 : BrowserMessageFilter(IndexedDBMsgStart
),
65 request_context_(request_context
),
66 indexed_db_context_(indexed_db_context
),
67 blob_storage_context_(blob_storage_context
),
68 database_dispatcher_host_(new DatabaseDispatcherHost(this)),
69 cursor_dispatcher_host_(new CursorDispatcherHost(this)),
70 ipc_process_id_(ipc_process_id
) {
71 DCHECK(indexed_db_context_
.get());
74 IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {
75 for (auto& iter
: blob_data_handle_map_
)
76 delete iter
.second
.first
;
79 void IndexedDBDispatcherHost::OnChannelConnected(int32 peer_pid
) {
80 BrowserMessageFilter::OnChannelConnected(peer_pid
);
82 if (request_context_getter_
.get()) {
83 DCHECK(!request_context_
);
84 request_context_
= request_context_getter_
->GetURLRequestContext();
85 request_context_getter_
= NULL
;
86 DCHECK(request_context_
);
90 void IndexedDBDispatcherHost::OnChannelClosing() {
91 bool success
= indexed_db_context_
->TaskRunner()->PostTask(
93 base::Bind(&IndexedDBDispatcherHost::ResetDispatcherHosts
, this));
96 ResetDispatcherHosts();
99 void IndexedDBDispatcherHost::OnDestruct() const {
100 // The last reference to the dispatcher may be a posted task, which would
101 // be destructed on the IndexedDB thread. Without this override, that would
102 // take the dispatcher with it. Since the dispatcher may be keeping the
103 // IndexedDBContext alive, it might be destructed to on its own thread,
104 // which is not supported. Ensure destruction runs on the IO thread instead.
105 BrowserThread::DeleteOnIOThread::Destruct(this);
108 void IndexedDBDispatcherHost::ResetDispatcherHosts() {
109 // It is important that the various *_dispatcher_host_ members are reset
110 // on the IndexedDB thread, since there might be incoming messages on that
111 // thread, and we must not reset the dispatcher hosts until after those
112 // messages are processed.
113 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
115 // Note that we explicitly separate CloseAll() from destruction of the
116 // DatabaseDispatcherHost, since CloseAll() can invoke callbacks which need to
117 // be dispatched through database_dispatcher_host_.
118 database_dispatcher_host_
->CloseAll();
119 database_dispatcher_host_
.reset();
120 cursor_dispatcher_host_
.reset();
123 base::TaskRunner
* IndexedDBDispatcherHost::OverrideTaskRunnerForMessage(
124 const IPC::Message
& message
) {
125 if (IPC_MESSAGE_CLASS(message
) != IndexedDBMsgStart
)
128 switch (message
.type()) {
129 case IndexedDBHostMsg_DatabasePut::ID
:
130 case IndexedDBHostMsg_AckReceivedBlobs::ID
:
133 return indexed_db_context_
->TaskRunner();
137 bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message
& message
) {
138 if (IPC_MESSAGE_CLASS(message
) != IndexedDBMsgStart
)
141 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread() ||
142 (message
.type() == IndexedDBHostMsg_DatabasePut::ID
||
143 message
.type() == IndexedDBHostMsg_AckReceivedBlobs::ID
));
145 bool handled
= database_dispatcher_host_
->OnMessageReceived(message
) ||
146 cursor_dispatcher_host_
->OnMessageReceived(message
);
150 IPC_BEGIN_MESSAGE_MAP(IndexedDBDispatcherHost
, message
)
151 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryGetDatabaseNames
,
152 OnIDBFactoryGetDatabaseNames
)
153 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryOpen
, OnIDBFactoryOpen
)
154 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryDeleteDatabase
,
155 OnIDBFactoryDeleteDatabase
)
156 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_AckReceivedBlobs
, OnAckReceivedBlobs
)
157 IPC_MESSAGE_UNHANDLED(handled
= false)
158 IPC_END_MESSAGE_MAP()
163 int32
IndexedDBDispatcherHost::Add(IndexedDBCursor
* cursor
) {
164 if (!cursor_dispatcher_host_
) {
167 return cursor_dispatcher_host_
->map_
.Add(cursor
);
170 int32
IndexedDBDispatcherHost::Add(IndexedDBConnection
* connection
,
172 const GURL
& origin_url
) {
173 if (!database_dispatcher_host_
) {
178 int32 ipc_database_id
= database_dispatcher_host_
->map_
.Add(connection
);
179 Context()->ConnectionOpened(origin_url
, connection
);
180 database_dispatcher_host_
->database_url_map_
[ipc_database_id
] = origin_url
;
181 return ipc_database_id
;
184 void IndexedDBDispatcherHost::RegisterTransactionId(int64 host_transaction_id
,
186 if (!database_dispatcher_host_
)
188 database_dispatcher_host_
->transaction_url_map_
[host_transaction_id
] = url
;
191 int64
IndexedDBDispatcherHost::HostTransactionId(int64 transaction_id
) {
192 // Inject the renderer process id into the transaction id, to
193 // uniquely identify this transaction, and effectively bind it to
194 // the renderer that initiated it. The lower 32 bits of
195 // transaction_id are guaranteed to be unique within that renderer.
196 base::ProcessId pid
= peer_pid();
197 DCHECK(!(transaction_id
>> 32)) << "Transaction ids can only be 32 bits";
198 static_assert(sizeof(base::ProcessId
) <= sizeof(int32
),
199 "Process ID must fit in 32 bits");
201 return transaction_id
| (static_cast<uint64
>(pid
) << 32);
204 int64
IndexedDBDispatcherHost::RendererTransactionId(
205 int64 host_transaction_id
) {
206 DCHECK(host_transaction_id
>> 32 == peer_pid())
207 << "Invalid renderer target for transaction id";
208 return host_transaction_id
& 0xffffffff;
212 uint32
IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
213 int64 host_transaction_id
) {
214 return host_transaction_id
& 0xffffffff;
218 uint32
IndexedDBDispatcherHost::TransactionIdToProcessId(
219 int64 host_transaction_id
) {
220 return (host_transaction_id
>> 32) & 0xffffffff;
223 std::string
IndexedDBDispatcherHost::HoldBlobData(
224 const IndexedDBBlobInfo
& blob_info
) {
225 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
226 std::string uuid
= blob_info
.uuid();
227 storage::BlobStorageContext
* context
= blob_storage_context_
->context();
228 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
230 uuid
= base::GenerateGUID();
231 storage::BlobDataBuilder
blob_data_builder(uuid
);
232 blob_data_builder
.set_content_type(base::UTF16ToUTF8(blob_info
.type()));
233 blob_data_builder
.AppendFile(blob_info
.file_path(), 0, blob_info
.size(),
234 blob_info
.last_modified());
235 blob_data_handle
= context
->AddFinishedBlob(&blob_data_builder
);
237 auto iter
= blob_data_handle_map_
.find(uuid
);
238 if (iter
!= blob_data_handle_map_
.end()) {
239 iter
->second
.second
+= 1;
242 blob_data_handle
= context
->GetBlobDataFromUUID(uuid
);
245 DCHECK(!ContainsKey(blob_data_handle_map_
, uuid
));
246 blob_data_handle_map_
[uuid
] = std::make_pair(blob_data_handle
.release(), 1);
250 void IndexedDBDispatcherHost::DropBlobData(const std::string
& uuid
) {
251 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
252 BlobDataHandleMap::iterator iter
= blob_data_handle_map_
.find(uuid
);
253 if (iter
!= blob_data_handle_map_
.end()) {
254 DCHECK_GE(iter
->second
.second
, 1);
255 if (iter
->second
.second
== 1) {
256 delete iter
->second
.first
;
257 blob_data_handle_map_
.erase(iter
);
259 iter
->second
.second
-= 1;
262 DLOG(FATAL
) << "Failed to find blob UUID in map:" << uuid
;
266 IndexedDBCursor
* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id
) {
267 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
268 return cursor_dispatcher_host_
->map_
.Lookup(ipc_cursor_id
);
271 ::IndexedDBDatabaseMetadata
IndexedDBDispatcherHost::ConvertMetadata(
272 const content::IndexedDBDatabaseMetadata
& web_metadata
) {
273 ::IndexedDBDatabaseMetadata metadata
;
274 metadata
.id
= web_metadata
.id
;
275 metadata
.name
= web_metadata
.name
;
276 metadata
.version
= web_metadata
.version
;
277 metadata
.int_version
= web_metadata
.int_version
;
278 metadata
.max_object_store_id
= web_metadata
.max_object_store_id
;
280 for (const auto& iter
: web_metadata
.object_stores
) {
281 const content::IndexedDBObjectStoreMetadata
& web_store_metadata
=
283 ::IndexedDBObjectStoreMetadata idb_store_metadata
;
284 idb_store_metadata
.id
= web_store_metadata
.id
;
285 idb_store_metadata
.name
= web_store_metadata
.name
;
286 idb_store_metadata
.key_path
= web_store_metadata
.key_path
;
287 idb_store_metadata
.auto_increment
= web_store_metadata
.auto_increment
;
288 idb_store_metadata
.max_index_id
= web_store_metadata
.max_index_id
;
290 for (const auto& index_iter
: web_store_metadata
.indexes
) {
291 const content::IndexedDBIndexMetadata
& web_index_metadata
=
293 ::IndexedDBIndexMetadata idb_index_metadata
;
294 idb_index_metadata
.id
= web_index_metadata
.id
;
295 idb_index_metadata
.name
= web_index_metadata
.name
;
296 idb_index_metadata
.key_path
= web_index_metadata
.key_path
;
297 idb_index_metadata
.unique
= web_index_metadata
.unique
;
298 idb_index_metadata
.multi_entry
= web_index_metadata
.multi_entry
;
299 idb_store_metadata
.indexes
.push_back(idb_index_metadata
);
301 metadata
.object_stores
.push_back(idb_store_metadata
);
306 void IndexedDBDispatcherHost::OnIDBFactoryGetDatabaseNames(
307 const IndexedDBHostMsg_FactoryGetDatabaseNames_Params
& params
) {
308 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
309 base::FilePath indexed_db_path
= indexed_db_context_
->data_path();
312 storage::GetOriginFromIdentifier(params
.database_identifier
);
314 Context()->GetIDBFactory()->GetDatabaseNames(
315 new IndexedDBCallbacks(
316 this, params
.ipc_thread_id
, params
.ipc_callbacks_id
),
322 void IndexedDBDispatcherHost::OnIDBFactoryOpen(
323 const IndexedDBHostMsg_FactoryOpen_Params
& params
) {
324 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
325 base::TimeTicks begin_time
= base::TimeTicks::Now();
326 base::FilePath indexed_db_path
= indexed_db_context_
->data_path();
329 storage::GetOriginFromIdentifier(params
.database_identifier
);
331 int64 host_transaction_id
= HostTransactionId(params
.transaction_id
);
333 // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
334 // created) if this origin is already over quota.
335 scoped_refptr
<IndexedDBCallbacks
> callbacks
=
336 new IndexedDBCallbacks(this,
337 params
.ipc_thread_id
,
338 params
.ipc_callbacks_id
,
339 params
.ipc_database_callbacks_id
,
342 callbacks
->SetConnectionOpenStartTime(begin_time
);
343 scoped_refptr
<IndexedDBDatabaseCallbacks
> database_callbacks
=
344 new IndexedDBDatabaseCallbacks(
345 this, params
.ipc_thread_id
, params
.ipc_database_callbacks_id
);
346 IndexedDBPendingConnection
connection(callbacks
,
351 DCHECK(request_context_
);
352 Context()->GetIDBFactory()->Open(
353 params
.name
, connection
, request_context_
, origin_url
, indexed_db_path
);
356 void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
357 const IndexedDBHostMsg_FactoryDeleteDatabase_Params
& params
) {
358 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
360 storage::GetOriginFromIdentifier(params
.database_identifier
);
361 base::FilePath indexed_db_path
= indexed_db_context_
->data_path();
362 DCHECK(request_context_
);
363 Context()->GetIDBFactory()->DeleteDatabase(
366 new IndexedDBCallbacks(
367 this, params
.ipc_thread_id
, params
.ipc_callbacks_id
),
372 // OnPutHelper exists only to allow us to hop threads while holding a reference
373 // to the IndexedDBDispatcherHost.
374 void IndexedDBDispatcherHost::OnPutHelper(
375 const IndexedDBHostMsg_DatabasePut_Params
& params
,
376 std::vector
<storage::BlobDataHandle
*> handles
) {
377 database_dispatcher_host_
->OnPut(params
, handles
);
380 void IndexedDBDispatcherHost::OnAckReceivedBlobs(
381 const std::vector
<std::string
>& uuids
) {
382 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
383 for (const auto& uuid
: uuids
)
387 void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id
,
389 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
390 if (!database_dispatcher_host_
)
392 TransactionIDToURLMap
& transaction_url_map
=
393 database_dispatcher_host_
->transaction_url_map_
;
394 TransactionIDToSizeMap
& transaction_size_map
=
395 database_dispatcher_host_
->transaction_size_map_
;
396 TransactionIDToDatabaseIDMap
& transaction_database_map
=
397 database_dispatcher_host_
->transaction_database_map_
;
399 Context()->TransactionComplete(transaction_url_map
[host_transaction_id
]);
400 transaction_url_map
.erase(host_transaction_id
);
401 transaction_size_map
.erase(host_transaction_id
);
402 transaction_database_map
.erase(host_transaction_id
);
405 //////////////////////////////////////////////////////////////////////
409 template <typename ObjectType
>
410 ObjectType
* IndexedDBDispatcherHost::GetOrTerminateProcess(
411 IDMap
<ObjectType
, IDMapOwnPointer
>* map
,
412 int32 ipc_return_object_id
) {
413 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
414 ObjectType
* return_object
= map
->Lookup(ipc_return_object_id
);
415 if (!return_object
) {
416 NOTREACHED() << "Uh oh, couldn't find object with id "
417 << ipc_return_object_id
;
418 bad_message::ReceivedBadMessage(this, bad_message::IDBDH_GET_OR_TERMINATE
);
420 return return_object
;
423 template <typename ObjectType
>
424 ObjectType
* IndexedDBDispatcherHost::GetOrTerminateProcess(
425 RefIDMap
<ObjectType
>* map
,
426 int32 ipc_return_object_id
) {
427 DCHECK(indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
428 ObjectType
* return_object
= map
->Lookup(ipc_return_object_id
);
429 if (!return_object
) {
430 NOTREACHED() << "Uh oh, couldn't find object with id "
431 << ipc_return_object_id
;
432 bad_message::ReceivedBadMessage(this, bad_message::IDBDH_GET_OR_TERMINATE
);
434 return return_object
;
437 template <typename MapType
>
438 void IndexedDBDispatcherHost::DestroyObject(MapType
* map
, int32 ipc_object_id
) {
439 GetOrTerminateProcess(map
, ipc_object_id
);
440 map
->Remove(ipc_object_id
);
443 //////////////////////////////////////////////////////////////////////
444 // IndexedDBDispatcherHost::DatabaseDispatcherHost
447 IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost(
448 IndexedDBDispatcherHost
* parent
)
450 map_
.set_check_on_null_data(true);
453 IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() {
454 // TODO(alecflett): uncomment these when we find the source of these leaks.
455 // DCHECK(transaction_size_map_.empty());
456 // DCHECK(transaction_url_map_.empty());
459 void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
461 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
462 // Abort outstanding transactions started by connections in the associated
463 // front-end to unblock later transactions. This should only occur on unclean
464 // (crash) or abrupt (process-kill) shutdowns.
465 for (TransactionIDToDatabaseIDMap::iterator iter
=
466 transaction_database_map_
.begin();
467 iter
!= transaction_database_map_
.end();) {
468 int64 transaction_id
= iter
->first
;
469 int32 ipc_database_id
= iter
->second
;
471 IndexedDBConnection
* connection
= map_
.Lookup(ipc_database_id
);
472 if (connection
&& connection
->IsConnected()) {
473 connection
->database()->Abort(
475 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
));
478 DCHECK(transaction_database_map_
.empty());
480 for (const auto& iter
: database_url_map_
) {
481 IndexedDBConnection
* connection
= map_
.Lookup(iter
.first
);
482 if (connection
&& connection
->IsConnected()) {
484 parent_
->Context()->ConnectionClosed(iter
.second
, connection
);
489 bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
490 const IPC::Message
& message
) {
492 (message
.type() == IndexedDBHostMsg_DatabasePut::ID
||
493 message
.type() == IndexedDBHostMsg_AckReceivedBlobs::ID
) ||
494 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
497 IPC_BEGIN_MESSAGE_MAP(
498 IndexedDBDispatcherHost::DatabaseDispatcherHost
, message
)
499 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore
,
501 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore
,
503 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction
,
505 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose
, OnClose
)
506 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseVersionChangeIgnored
,
507 OnVersionChangeIgnored
)
508 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed
, OnDestroyed
)
509 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet
, OnGet
)
510 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGetAll
, OnGetAll
)
511 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut
, OnPutWrapper
)
512 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys
, OnSetIndexKeys
)
513 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady
,
515 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseOpenCursor
, OnOpenCursor
)
516 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCount
, OnCount
)
517 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteRange
, OnDeleteRange
)
518 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClear
, OnClear
)
519 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateIndex
, OnCreateIndex
)
520 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteIndex
, OnDeleteIndex
)
521 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseAbort
, OnAbort
)
522 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit
, OnCommit
)
523 IPC_MESSAGE_UNHANDLED(handled
= false)
524 IPC_END_MESSAGE_MAP()
529 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore(
530 const IndexedDBHostMsg_DatabaseCreateObjectStore_Params
& params
) {
532 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
533 IndexedDBConnection
* connection
=
534 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
535 if (!connection
|| !connection
->IsConnected())
538 int64 host_transaction_id
= parent_
->HostTransactionId(params
.transaction_id
);
539 connection
->database()->CreateObjectStore(host_transaction_id
,
540 params
.object_store_id
,
543 params
.auto_increment
);
544 if (parent_
->Context()->IsOverQuota(
545 database_url_map_
[params
.ipc_database_id
])) {
546 connection
->database()->Abort(
548 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError
));
552 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore(
553 int32 ipc_database_id
,
554 int64 transaction_id
,
555 int64 object_store_id
) {
557 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
558 IndexedDBConnection
* connection
=
559 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
560 if (!connection
|| !connection
->IsConnected())
563 connection
->database()->DeleteObjectStore(
564 parent_
->HostTransactionId(transaction_id
), object_store_id
);
567 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction(
568 const IndexedDBHostMsg_DatabaseCreateTransaction_Params
& params
) {
570 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
571 IndexedDBConnection
* connection
=
572 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
573 if (!connection
|| !connection
->IsConnected())
576 int64 host_transaction_id
= parent_
->HostTransactionId(params
.transaction_id
);
578 if (transaction_database_map_
.find(host_transaction_id
) !=
579 transaction_database_map_
.end()) {
580 DLOG(ERROR
) << "Duplicate host_transaction_id.";
584 connection
->database()->CreateTransaction(
585 host_transaction_id
, connection
, params
.object_store_ids
, params
.mode
);
586 transaction_database_map_
[host_transaction_id
] = params
.ipc_database_id
;
587 parent_
->RegisterTransactionId(host_transaction_id
,
588 database_url_map_
[params
.ipc_database_id
]);
591 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose(
592 int32 ipc_database_id
) {
594 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
595 IndexedDBConnection
* connection
=
596 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
597 if (!connection
|| !connection
->IsConnected())
602 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnVersionChangeIgnored(
603 int32 ipc_database_id
) {
605 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
606 IndexedDBConnection
* connection
=
607 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
608 if (!connection
|| !connection
->IsConnected())
610 connection
->VersionChangeIgnored();
613 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
614 int32 ipc_object_id
) {
616 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
617 IndexedDBConnection
* connection
=
618 parent_
->GetOrTerminateProcess(&map_
, ipc_object_id
);
621 if (connection
->IsConnected())
624 ->ConnectionClosed(database_url_map_
[ipc_object_id
], connection
);
625 database_url_map_
.erase(ipc_object_id
);
626 parent_
->DestroyObject(&map_
, ipc_object_id
);
629 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet(
630 const IndexedDBHostMsg_DatabaseGet_Params
& params
) {
632 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
633 IndexedDBConnection
* connection
=
634 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
635 if (!connection
|| !connection
->IsConnected())
638 scoped_refptr
<IndexedDBCallbacks
> callbacks(new IndexedDBCallbacks(
639 parent_
, params
.ipc_thread_id
, params
.ipc_callbacks_id
));
640 connection
->database()->Get(
641 parent_
->HostTransactionId(params
.transaction_id
),
642 params
.object_store_id
,
644 make_scoped_ptr(new IndexedDBKeyRange(params
.key_range
)),
649 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGetAll(
650 const IndexedDBHostMsg_DatabaseGetAll_Params
& params
) {
652 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
653 IndexedDBConnection
* connection
=
654 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
655 if (!connection
|| !connection
->IsConnected())
658 scoped_refptr
<IndexedDBCallbacks
> callbacks(new IndexedDBCallbacks(
659 parent_
, params
.ipc_thread_id
, params
.ipc_callbacks_id
));
660 connection
->database()->GetAll(
661 parent_
->HostTransactionId(params
.transaction_id
), params
.object_store_id
,
662 params
.index_id
, make_scoped_ptr(new IndexedDBKeyRange(params
.key_range
)),
663 params
.key_only
, params
.max_count
, callbacks
);
666 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPutWrapper(
667 const IndexedDBHostMsg_DatabasePut_Params
& params
) {
668 std::vector
<storage::BlobDataHandle
*> handles
;
669 for (size_t i
= 0; i
< params
.value
.blob_or_file_info
.size(); ++i
) {
670 const IndexedDBMsg_BlobOrFileInfo
& info
= params
.value
.blob_or_file_info
[i
];
671 handles
.push_back(parent_
->blob_storage_context_
->context()
672 ->GetBlobDataFromUUID(info
.uuid
)
675 parent_
->indexed_db_context_
->TaskRunner()->PostTask(
678 &IndexedDBDispatcherHost::OnPutHelper
, parent_
, params
, handles
));
681 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
682 const IndexedDBHostMsg_DatabasePut_Params
& params
,
683 std::vector
<storage::BlobDataHandle
*> handles
) {
685 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
687 ScopedVector
<storage::BlobDataHandle
> scoped_handles
;
688 scoped_handles
.swap(handles
);
690 IndexedDBConnection
* connection
=
691 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
692 if (!connection
|| !connection
->IsConnected())
694 scoped_refptr
<IndexedDBCallbacks
> callbacks(new IndexedDBCallbacks(
695 parent_
, params
.ipc_thread_id
, params
.ipc_callbacks_id
));
697 int64 host_transaction_id
= parent_
->HostTransactionId(params
.transaction_id
);
699 std::vector
<IndexedDBBlobInfo
> blob_info(
700 params
.value
.blob_or_file_info
.size());
702 ChildProcessSecurityPolicyImpl
* policy
=
703 ChildProcessSecurityPolicyImpl::GetInstance();
705 for (size_t i
= 0; i
< params
.value
.blob_or_file_info
.size(); ++i
) {
706 const IndexedDBMsg_BlobOrFileInfo
& info
= params
.value
.blob_or_file_info
[i
];
709 if (!info
.file_path
.empty()) {
710 path
= base::FilePath::FromUTF16Unsafe(info
.file_path
);
711 if (!policy
->CanReadFile(parent_
->ipc_process_id_
, path
)) {
712 bad_message::ReceivedBadMessage(parent_
,
713 bad_message::IDBDH_CAN_READ_FILE
);
718 IndexedDBBlobInfo(info
.uuid
, path
, info
.file_name
, info
.mime_type
);
719 if (info
.size
!= static_cast<uint64_t>(-1)) {
720 blob_info
[i
].set_last_modified(
721 base::Time::FromDoubleT(info
.last_modified
));
722 blob_info
[i
].set_size(info
.size
);
725 blob_info
[i
] = IndexedDBBlobInfo(info
.uuid
, info
.mime_type
, info
.size
);
729 // TODO(alecflett): Avoid a copy here.
730 IndexedDBValue value
;
731 value
.bits
= params
.value
.bits
;
732 value
.blob_info
.swap(blob_info
);
733 connection
->database()->Put(host_transaction_id
,
734 params
.object_store_id
,
737 make_scoped_ptr(new IndexedDBKey(params
.key
)),
741 TransactionIDToSizeMap
* map
=
742 &parent_
->database_dispatcher_host_
->transaction_size_map_
;
743 // Size can't be big enough to overflow because it represents the
744 // actual bytes passed through IPC.
745 (*map
)[host_transaction_id
] += params
.value
.bits
.size();
748 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys(
749 const IndexedDBHostMsg_DatabaseSetIndexKeys_Params
& params
) {
751 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
752 IndexedDBConnection
* connection
=
753 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
754 if (!connection
|| !connection
->IsConnected())
757 int64 host_transaction_id
= parent_
->HostTransactionId(params
.transaction_id
);
758 connection
->database()->SetIndexKeys(
760 params
.object_store_id
,
761 make_scoped_ptr(new IndexedDBKey(params
.primary_key
)),
765 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady(
766 int32 ipc_database_id
,
767 int64 transaction_id
,
768 int64 object_store_id
,
769 const std::vector
<int64
>& index_ids
) {
771 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
772 IndexedDBConnection
* connection
=
773 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
774 if (!connection
|| !connection
->IsConnected())
777 connection
->database()->SetIndexesReady(
778 parent_
->HostTransactionId(transaction_id
), object_store_id
, index_ids
);
781 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpenCursor(
782 const IndexedDBHostMsg_DatabaseOpenCursor_Params
& params
) {
784 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
785 IndexedDBConnection
* connection
=
786 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
787 if (!connection
|| !connection
->IsConnected())
790 scoped_refptr
<IndexedDBCallbacks
> callbacks(new IndexedDBCallbacks(
791 parent_
, params
.ipc_thread_id
, params
.ipc_callbacks_id
, -1));
792 connection
->database()->OpenCursor(
793 parent_
->HostTransactionId(params
.transaction_id
),
794 params
.object_store_id
,
796 make_scoped_ptr(new IndexedDBKeyRange(params
.key_range
)),
803 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCount(
804 const IndexedDBHostMsg_DatabaseCount_Params
& params
) {
806 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
807 IndexedDBConnection
* connection
=
808 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
809 if (!connection
|| !connection
->IsConnected())
812 scoped_refptr
<IndexedDBCallbacks
> callbacks(new IndexedDBCallbacks(
813 parent_
, params
.ipc_thread_id
, params
.ipc_callbacks_id
));
814 connection
->database()->Count(
815 parent_
->HostTransactionId(params
.transaction_id
),
816 params
.object_store_id
,
818 make_scoped_ptr(new IndexedDBKeyRange(params
.key_range
)),
822 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteRange(
823 const IndexedDBHostMsg_DatabaseDeleteRange_Params
& params
) {
825 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
826 IndexedDBConnection
* connection
=
827 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
828 if (!connection
|| !connection
->IsConnected())
831 scoped_refptr
<IndexedDBCallbacks
> callbacks(new IndexedDBCallbacks(
832 parent_
, params
.ipc_thread_id
, params
.ipc_callbacks_id
));
833 connection
->database()->DeleteRange(
834 parent_
->HostTransactionId(params
.transaction_id
),
835 params
.object_store_id
,
836 make_scoped_ptr(new IndexedDBKeyRange(params
.key_range
)),
840 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear(
842 int32 ipc_callbacks_id
,
843 int32 ipc_database_id
,
844 int64 transaction_id
,
845 int64 object_store_id
) {
847 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
848 IndexedDBConnection
* connection
=
849 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
850 if (!connection
|| !connection
->IsConnected())
853 scoped_refptr
<IndexedDBCallbacks
> callbacks(
854 new IndexedDBCallbacks(parent_
, ipc_thread_id
, ipc_callbacks_id
));
856 connection
->database()->Clear(
857 parent_
->HostTransactionId(transaction_id
), object_store_id
, callbacks
);
860 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnAbort(
861 int32 ipc_database_id
,
862 int64 transaction_id
) {
864 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
865 IndexedDBConnection
* connection
=
866 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
867 if (!connection
|| !connection
->IsConnected())
870 connection
->database()->Abort(parent_
->HostTransactionId(transaction_id
));
873 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit(
874 int32 ipc_database_id
,
875 int64 transaction_id
) {
877 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
878 IndexedDBConnection
* connection
=
879 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
880 if (!connection
|| !connection
->IsConnected())
883 int64 host_transaction_id
= parent_
->HostTransactionId(transaction_id
);
884 int64 transaction_size
= transaction_size_map_
[host_transaction_id
];
885 if (transaction_size
&&
886 parent_
->Context()->WouldBeOverQuota(
887 transaction_url_map_
[host_transaction_id
], transaction_size
)) {
888 connection
->database()->Abort(
890 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError
));
894 connection
->database()->Commit(host_transaction_id
);
897 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateIndex(
898 const IndexedDBHostMsg_DatabaseCreateIndex_Params
& params
) {
900 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
901 IndexedDBConnection
* connection
=
902 parent_
->GetOrTerminateProcess(&map_
, params
.ipc_database_id
);
903 if (!connection
|| !connection
->IsConnected())
906 int64 host_transaction_id
= parent_
->HostTransactionId(params
.transaction_id
);
907 connection
->database()->CreateIndex(host_transaction_id
,
908 params
.object_store_id
,
914 if (parent_
->Context()->IsOverQuota(
915 database_url_map_
[params
.ipc_database_id
])) {
916 connection
->database()->Abort(
918 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError
));
922 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteIndex(
923 int32 ipc_database_id
,
924 int64 transaction_id
,
925 int64 object_store_id
,
928 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
929 IndexedDBConnection
* connection
=
930 parent_
->GetOrTerminateProcess(&map_
, ipc_database_id
);
931 if (!connection
|| !connection
->IsConnected())
934 connection
->database()->DeleteIndex(
935 parent_
->HostTransactionId(transaction_id
), object_store_id
, index_id
);
938 //////////////////////////////////////////////////////////////////////
939 // IndexedDBDispatcherHost::CursorDispatcherHost
942 IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost(
943 IndexedDBDispatcherHost
* parent
)
945 map_
.set_check_on_null_data(true);
948 IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {}
950 bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
951 const IPC::Message
& message
) {
953 IPC_BEGIN_MESSAGE_MAP(
954 IndexedDBDispatcherHost::CursorDispatcherHost
, message
)
955 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance
, OnAdvance
)
956 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue
, OnContinue
)
957 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch
, OnPrefetch
)
958 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset
, OnPrefetchReset
)
959 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed
, OnDestroyed
)
960 IPC_MESSAGE_UNHANDLED(handled
= false)
961 IPC_END_MESSAGE_MAP()
965 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
970 void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance(
973 int32 ipc_callbacks_id
,
976 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
977 IndexedDBCursor
* idb_cursor
=
978 parent_
->GetOrTerminateProcess(&map_
, ipc_cursor_id
);
984 new IndexedDBCallbacks(
985 parent_
, ipc_thread_id
, ipc_callbacks_id
, ipc_cursor_id
));
988 void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
991 int32 ipc_callbacks_id
,
992 const IndexedDBKey
& key
,
993 const IndexedDBKey
& primary_key
) {
995 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
996 IndexedDBCursor
* idb_cursor
=
997 parent_
->GetOrTerminateProcess(&map_
, ipc_cursor_id
);
1001 idb_cursor
->Continue(
1002 key
.IsValid() ? make_scoped_ptr(new IndexedDBKey(key
))
1003 : scoped_ptr
<IndexedDBKey
>(),
1004 primary_key
.IsValid() ? make_scoped_ptr(new IndexedDBKey(primary_key
))
1005 : scoped_ptr
<IndexedDBKey
>(),
1006 new IndexedDBCallbacks(
1007 parent_
, ipc_thread_id
, ipc_callbacks_id
, ipc_cursor_id
));
1010 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
1011 int32 ipc_cursor_id
,
1012 int32 ipc_thread_id
,
1013 int32 ipc_callbacks_id
,
1016 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
1017 IndexedDBCursor
* idb_cursor
=
1018 parent_
->GetOrTerminateProcess(&map_
, ipc_cursor_id
);
1022 idb_cursor
->PrefetchContinue(
1024 new IndexedDBCallbacks(
1025 parent_
, ipc_thread_id
, ipc_callbacks_id
, ipc_cursor_id
));
1028 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
1029 int32 ipc_cursor_id
,
1030 int used_prefetches
,
1031 int unused_prefetches
) {
1033 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
1034 IndexedDBCursor
* idb_cursor
=
1035 parent_
->GetOrTerminateProcess(&map_
, ipc_cursor_id
);
1040 idb_cursor
->PrefetchReset(used_prefetches
, unused_prefetches
);
1041 // TODO(cmumford): Handle this error (crbug.com/363397)
1043 DLOG(ERROR
) << "Unable to reset prefetch";
1046 void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed(
1047 int32 ipc_object_id
) {
1049 parent_
->indexed_db_context_
->TaskRunner()->RunsTasksOnCurrentThread());
1050 parent_
->DestroyObject(&map_
, ipc_object_id
);
1053 } // namespace content