1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/files/file_path.h" 10 #include "base/memory/scoped_vector.h" 11 #include "base/process/process.h" 12 #include "base/stl_util.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "content/browser/child_process_security_policy_impl.h" 15 #include "content/browser/indexed_db/indexed_db_callbacks.h" 16 #include "content/browser/indexed_db/indexed_db_connection.h" 17 #include "content/browser/indexed_db/indexed_db_context_impl.h" 18 #include "content/browser/indexed_db/indexed_db_cursor.h" 19 #include "content/browser/indexed_db/indexed_db_database_callbacks.h" 20 #include "content/browser/indexed_db/indexed_db_metadata.h" 21 #include "content/browser/indexed_db/indexed_db_pending_connection.h" 22 #include "content/browser/indexed_db/indexed_db_value.h" 23 #include "content/browser/renderer_host/render_message_filter.h" 24 #include "content/common/indexed_db/indexed_db_messages.h" 25 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/user_metrics.h" 27 #include "content/public/common/content_switches.h" 28 #include "content/public/common/result_codes.h" 29 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" 30 #include "url/gurl.h" 31 #include "webkit/browser/blob/blob_storage_context.h" 32 #include "webkit/browser/database/database_util.h" 33 #include "webkit/common/database/database_identifier.h" 34 35 using webkit_database::DatabaseUtil; 36 using blink::WebIDBKey; 37 38 namespace content { 39 40 IndexedDBDispatcherHost::IndexedDBDispatcherHost( 41 int ipc_process_id, 42 net::URLRequestContextGetter* request_context_getter, 43 IndexedDBContextImpl* indexed_db_context, 44 ChromeBlobStorageContext* blob_storage_context) 45 : BrowserMessageFilter(IndexedDBMsgStart), 46 request_context_getter_(request_context_getter), 47 request_context_(NULL), 48 indexed_db_context_(indexed_db_context), 49 blob_storage_context_(blob_storage_context), 50 database_dispatcher_host_(new DatabaseDispatcherHost(this)), 51 cursor_dispatcher_host_(new CursorDispatcherHost(this)), 52 ipc_process_id_(ipc_process_id) { 53 DCHECK(indexed_db_context_); 54 } 55 56 IndexedDBDispatcherHost::IndexedDBDispatcherHost( 57 int ipc_process_id, 58 net::URLRequestContext* request_context, 59 IndexedDBContextImpl* indexed_db_context, 60 ChromeBlobStorageContext* blob_storage_context) 61 : BrowserMessageFilter(IndexedDBMsgStart), 62 request_context_(request_context), 63 indexed_db_context_(indexed_db_context), 64 blob_storage_context_(blob_storage_context), 65 database_dispatcher_host_(new DatabaseDispatcherHost(this)), 66 cursor_dispatcher_host_(new CursorDispatcherHost(this)), 67 ipc_process_id_(ipc_process_id) { 68 DCHECK(indexed_db_context_); 69 } 70 71 IndexedDBDispatcherHost::~IndexedDBDispatcherHost() { 72 STLDeleteValues(&blob_data_handle_map_); 73 } 74 75 void IndexedDBDispatcherHost::OnChannelConnected(int32 peer_pid) { 76 BrowserMessageFilter::OnChannelConnected(peer_pid); 77 78 if (request_context_getter_.get()) { 79 DCHECK(!request_context_); 80 request_context_ = request_context_getter_->GetURLRequestContext(); 81 request_context_getter_ = NULL; 82 DCHECK(request_context_); 83 } 84 } 85 86 void IndexedDBDispatcherHost::OnChannelClosing() { 87 bool success = indexed_db_context_->TaskRunner()->PostTask( 88 FROM_HERE, 89 base::Bind(&IndexedDBDispatcherHost::ResetDispatcherHosts, this)); 90 91 if (!success) 92 ResetDispatcherHosts(); 93 } 94 95 void IndexedDBDispatcherHost::OnDestruct() const { 96 // The last reference to the dispatcher may be a posted task, which would 97 // be destructed on the IndexedDB thread. Without this override, that would 98 // take the dispatcher with it. Since the dispatcher may be keeping the 99 // IndexedDBContext alive, it might be destructed to on its own thread, 100 // which is not supported. Ensure destruction runs on the IO thread instead. 101 BrowserThread::DeleteOnIOThread::Destruct(this); 102 } 103 104 void IndexedDBDispatcherHost::ResetDispatcherHosts() { 105 // It is important that the various *_dispatcher_host_ members are reset 106 // on the IndexedDB thread, since there might be incoming messages on that 107 // thread, and we must not reset the dispatcher hosts until after those 108 // messages are processed. 109 DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 110 111 // Note that we explicitly separate CloseAll() from destruction of the 112 // DatabaseDispatcherHost, since CloseAll() can invoke callbacks which need to 113 // be dispatched through database_dispatcher_host_. 114 database_dispatcher_host_->CloseAll(); 115 database_dispatcher_host_.reset(); 116 cursor_dispatcher_host_.reset(); 117 } 118 119 base::TaskRunner* IndexedDBDispatcherHost::OverrideTaskRunnerForMessage( 120 const IPC::Message& message) { 121 if (IPC_MESSAGE_CLASS(message) == IndexedDBMsgStart && 122 message.type() != IndexedDBHostMsg_DatabasePut::ID) 123 return indexed_db_context_->TaskRunner(); 124 return NULL; 125 } 126 127 bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message& message) { 128 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 152 int32 IndexedDBDispatcherHost::Add(IndexedDBCursor* cursor) { 153 if (!cursor_dispatcher_host_) { 154 return 0; 155 } 156 return cursor_dispatcher_host_->map_.Add(cursor); 157 } 158 159 int32 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 173 void 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 180 int64 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 193 int64 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 201 uint32 IndexedDBDispatcherHost::TransactionIdToRendererTransactionId( 202 int64 host_transaction_id) { 203 return host_transaction_id & 0xffffffff; 204 } 205 206 // static 207 uint32 IndexedDBDispatcherHost::TransactionIdToProcessId( 208 int64 host_transaction_id) { 209 return (host_transaction_id >> 32) & 0xffffffff; 210 } 211 212 void IndexedDBDispatcherHost::HoldBlobDataHandle( 213 const std::string& uuid, 214 scoped_ptr<webkit_blob::BlobDataHandle>& blob_data_handle) { 215 DCHECK(!ContainsKey(blob_data_handle_map_, uuid)); 216 blob_data_handle_map_[uuid] = blob_data_handle.release(); 217 } 218 219 void 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 229 IndexedDBCursor* 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 275 void 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 webkit_database::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 291 void IndexedDBDispatcherHost::OnIDBFactoryOpen( 292 const IndexedDBHostMsg_FactoryOpen_Params& params) { 293 DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 294 base::FilePath indexed_db_path = indexed_db_context_->data_path(); 295 296 GURL origin_url = 297 webkit_database::GetOriginFromIdentifier(params.database_identifier); 298 299 int64 host_transaction_id = HostTransactionId(params.transaction_id); 300 301 // TODO(dgrogan): Don't let a non-existing database be opened (and therefore 302 // created) if this origin is already over quota. 303 scoped_refptr<IndexedDBCallbacks> callbacks = 304 new IndexedDBCallbacks(this, 305 params.ipc_thread_id, 306 params.ipc_callbacks_id, 307 params.ipc_database_callbacks_id, 308 host_transaction_id, 309 origin_url); 310 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks = 311 new IndexedDBDatabaseCallbacks( 312 this, params.ipc_thread_id, params.ipc_database_callbacks_id); 313 IndexedDBPendingConnection connection(callbacks, 314 database_callbacks, 315 ipc_process_id_, 316 host_transaction_id, 317 params.version); 318 DCHECK(request_context_); 319 Context()->GetIDBFactory()->Open( 320 params.name, connection, request_context_, origin_url, indexed_db_path); 321 } 322 323 void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase( 324 const IndexedDBHostMsg_FactoryDeleteDatabase_Params& params) { 325 DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 326 GURL origin_url = 327 webkit_database::GetOriginFromIdentifier(params.database_identifier); 328 base::FilePath indexed_db_path = indexed_db_context_->data_path(); 329 DCHECK(request_context_); 330 Context()->GetIDBFactory()->DeleteDatabase( 331 params.name, 332 request_context_, 333 new IndexedDBCallbacks( 334 this, params.ipc_thread_id, params.ipc_callbacks_id), 335 origin_url, 336 indexed_db_path); 337 } 338 339 // OnPutHelper exists only to allow us to hop threads while holding a reference 340 // to the IndexedDBDispatcherHost. 341 void IndexedDBDispatcherHost::OnPutHelper( 342 const IndexedDBHostMsg_DatabasePut_Params& params, 343 std::vector<webkit_blob::BlobDataHandle*> handles) { 344 database_dispatcher_host_->OnPut(params, handles); 345 } 346 347 void IndexedDBDispatcherHost::OnAckReceivedBlobs( 348 const std::vector<std::string>& uuids) { 349 DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 350 std::vector<std::string>::const_iterator iter; 351 for (iter = uuids.begin(); iter != uuids.end(); ++iter) 352 DropBlobDataHandle(*iter); 353 } 354 355 void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id, 356 bool committed) { 357 DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 358 if (!database_dispatcher_host_) 359 return; 360 TransactionIDToURLMap& transaction_url_map = 361 database_dispatcher_host_->transaction_url_map_; 362 TransactionIDToSizeMap& transaction_size_map = 363 database_dispatcher_host_->transaction_size_map_; 364 TransactionIDToDatabaseIDMap& transaction_database_map = 365 database_dispatcher_host_->transaction_database_map_; 366 if (committed) 367 Context()->TransactionComplete(transaction_url_map[host_transaction_id]); 368 transaction_url_map.erase(host_transaction_id); 369 transaction_size_map.erase(host_transaction_id); 370 transaction_database_map.erase(host_transaction_id); 371 } 372 373 ////////////////////////////////////////////////////////////////////// 374 // Helper templates. 375 // 376 377 template <typename ObjectType> 378 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess( 379 IDMap<ObjectType, IDMapOwnPointer>* map, 380 int32 ipc_return_object_id) { 381 DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 382 ObjectType* return_object = map->Lookup(ipc_return_object_id); 383 if (!return_object) { 384 NOTREACHED() << "Uh oh, couldn't find object with id " 385 << ipc_return_object_id; 386 RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF")); 387 BadMessageReceived(); 388 } 389 return return_object; 390 } 391 392 template <typename ObjectType> 393 ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess( 394 RefIDMap<ObjectType>* map, 395 int32 ipc_return_object_id) { 396 DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 397 ObjectType* return_object = map->Lookup(ipc_return_object_id); 398 if (!return_object) { 399 NOTREACHED() << "Uh oh, couldn't find object with id " 400 << ipc_return_object_id; 401 RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF")); 402 BadMessageReceived(); 403 } 404 return return_object; 405 } 406 407 template <typename MapType> 408 void IndexedDBDispatcherHost::DestroyObject(MapType* map, int32 ipc_object_id) { 409 GetOrTerminateProcess(map, ipc_object_id); 410 map->Remove(ipc_object_id); 411 } 412 413 ////////////////////////////////////////////////////////////////////// 414 // IndexedDBDispatcherHost::DatabaseDispatcherHost 415 // 416 417 IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost( 418 IndexedDBDispatcherHost* parent) 419 : parent_(parent) { 420 map_.set_check_on_null_data(true); 421 } 422 423 IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() { 424 // TODO(alecflett): uncomment these when we find the source of these leaks. 425 // DCHECK(transaction_size_map_.empty()); 426 // DCHECK(transaction_url_map_.empty()); 427 } 428 429 void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() { 430 DCHECK( 431 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 432 // Abort outstanding transactions started by connections in the associated 433 // front-end to unblock later transactions. This should only occur on unclean 434 // (crash) or abrupt (process-kill) shutdowns. 435 for (TransactionIDToDatabaseIDMap::iterator iter = 436 transaction_database_map_.begin(); 437 iter != transaction_database_map_.end();) { 438 int64 transaction_id = iter->first; 439 int32 ipc_database_id = iter->second; 440 ++iter; 441 IndexedDBConnection* connection = map_.Lookup(ipc_database_id); 442 if (connection && connection->IsConnected()) { 443 connection->database()->Abort( 444 transaction_id, 445 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError)); 446 } 447 } 448 DCHECK(transaction_database_map_.empty()); 449 450 for (WebIDBObjectIDToURLMap::iterator iter = database_url_map_.begin(); 451 iter != database_url_map_.end(); 452 iter++) { 453 IndexedDBConnection* connection = map_.Lookup(iter->first); 454 if (connection && connection->IsConnected()) { 455 connection->Close(); 456 parent_->Context()->ConnectionClosed(iter->second, connection); 457 } 458 } 459 } 460 461 bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived( 462 const IPC::Message& message) { 463 464 DCHECK( 465 (message.type() == IndexedDBHostMsg_DatabasePut::ID) || 466 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 467 468 bool handled = true; 469 IPC_BEGIN_MESSAGE_MAP( 470 IndexedDBDispatcherHost::DatabaseDispatcherHost, message) 471 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore, 472 OnCreateObjectStore) 473 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore, 474 OnDeleteObjectStore) 475 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction, 476 OnCreateTransaction) 477 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose) 478 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed, OnDestroyed) 479 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet, OnGet) 480 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPutWrapper) 481 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys, OnSetIndexKeys) 482 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady, 483 OnSetIndexesReady) 484 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseOpenCursor, OnOpenCursor) 485 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCount, OnCount) 486 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteRange, OnDeleteRange) 487 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClear, OnClear) 488 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateIndex, OnCreateIndex) 489 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteIndex, OnDeleteIndex) 490 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseAbort, OnAbort) 491 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit, OnCommit) 492 IPC_MESSAGE_UNHANDLED(handled = false) 493 IPC_END_MESSAGE_MAP() 494 495 return handled; 496 } 497 498 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore( 499 const IndexedDBHostMsg_DatabaseCreateObjectStore_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 || !connection->IsConnected()) 505 return; 506 507 int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id); 508 connection->database()->CreateObjectStore(host_transaction_id, 509 params.object_store_id, 510 params.name, 511 params.key_path, 512 params.auto_increment); 513 if (parent_->Context()->IsOverQuota( 514 database_url_map_[params.ipc_database_id])) { 515 connection->database()->Abort( 516 host_transaction_id, 517 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError)); 518 } 519 } 520 521 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore( 522 int32 ipc_database_id, 523 int64 transaction_id, 524 int64 object_store_id) { 525 DCHECK( 526 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 527 IndexedDBConnection* connection = 528 parent_->GetOrTerminateProcess(&map_, ipc_database_id); 529 if (!connection || !connection->IsConnected()) 530 return; 531 532 connection->database()->DeleteObjectStore( 533 parent_->HostTransactionId(transaction_id), object_store_id); 534 } 535 536 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction( 537 const IndexedDBHostMsg_DatabaseCreateTransaction_Params& params) { 538 DCHECK( 539 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 540 IndexedDBConnection* connection = 541 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 542 if (!connection || !connection->IsConnected()) 543 return; 544 545 int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id); 546 547 if (transaction_database_map_.find(host_transaction_id) != 548 transaction_database_map_.end()) { 549 DLOG(ERROR) << "Duplicate host_transaction_id."; 550 return; 551 } 552 553 connection->database()->CreateTransaction( 554 host_transaction_id, connection, params.object_store_ids, params.mode); 555 transaction_database_map_[host_transaction_id] = params.ipc_database_id; 556 parent_->RegisterTransactionId(host_transaction_id, 557 database_url_map_[params.ipc_database_id]); 558 } 559 560 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose( 561 int32 ipc_database_id) { 562 DCHECK( 563 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 564 IndexedDBConnection* connection = 565 parent_->GetOrTerminateProcess(&map_, ipc_database_id); 566 if (!connection || !connection->IsConnected()) 567 return; 568 connection->Close(); 569 } 570 571 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed( 572 int32 ipc_object_id) { 573 DCHECK( 574 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 575 IndexedDBConnection* connection = map_.Lookup(ipc_object_id); 576 if (connection->IsConnected()) 577 connection->Close(); 578 parent_->Context() 579 ->ConnectionClosed(database_url_map_[ipc_object_id], connection); 580 database_url_map_.erase(ipc_object_id); 581 parent_->DestroyObject(&map_, ipc_object_id); 582 } 583 584 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet( 585 const IndexedDBHostMsg_DatabaseGet_Params& params) { 586 DCHECK( 587 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 588 IndexedDBConnection* connection = 589 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 590 if (!connection || !connection->IsConnected()) 591 return; 592 593 scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks( 594 parent_, params.ipc_thread_id, params.ipc_callbacks_id)); 595 connection->database()->Get( 596 parent_->HostTransactionId(params.transaction_id), 597 params.object_store_id, 598 params.index_id, 599 make_scoped_ptr(new IndexedDBKeyRange(params.key_range)), 600 params.key_only, 601 callbacks); 602 } 603 604 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPutWrapper( 605 const IndexedDBHostMsg_DatabasePut_Params& params) { 606 std::vector<webkit_blob::BlobDataHandle*> handles; 607 for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) { 608 const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i]; 609 handles.push_back(parent_->blob_storage_context_->context() 610 ->GetBlobDataFromUUID(info.uuid) 611 .release()); 612 } 613 parent_->indexed_db_context_->TaskRunner()->PostTask( 614 FROM_HERE, 615 base::Bind( 616 &IndexedDBDispatcherHost::OnPutHelper, parent_, params, handles)); 617 } 618 619 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut( 620 const IndexedDBHostMsg_DatabasePut_Params& params, 621 std::vector<webkit_blob::BlobDataHandle*> handles) { 622 623 DCHECK( 624 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 625 626 ScopedVector<webkit_blob::BlobDataHandle> scoped_handles; 627 scoped_handles.swap(handles); 628 629 IndexedDBConnection* connection = 630 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 631 if (!connection || !connection->IsConnected()) 632 return; 633 scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks( 634 parent_, params.ipc_thread_id, params.ipc_callbacks_id)); 635 636 int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id); 637 638 std::vector<IndexedDBBlobInfo> blob_info(params.blob_or_file_info.size()); 639 640 ChildProcessSecurityPolicyImpl* policy = 641 ChildProcessSecurityPolicyImpl::GetInstance(); 642 643 for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) { 644 const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i]; 645 if (info.is_file) { 646 base::FilePath path = base::FilePath::FromUTF16Unsafe(info.file_path); 647 if (!policy->CanReadFile(parent_->ipc_process_id_, path)) { 648 parent_->BadMessageReceived(); 649 return; 650 } 651 blob_info[i] = 652 IndexedDBBlobInfo(info.uuid, path, info.file_name, info.mime_type); 653 if (info.size != static_cast<uint64_t>(-1)) { 654 blob_info[i].set_last_modified( 655 base::Time::FromDoubleT(info.last_modified)); 656 blob_info[i].set_size(info.size); 657 } 658 } else { 659 blob_info[i] = IndexedDBBlobInfo(info.uuid, info.mime_type, info.size); 660 } 661 } 662 663 // TODO(alecflett): Avoid a copy here. 664 IndexedDBValue value; 665 value.bits = params.value; 666 value.blob_info.swap(blob_info); 667 connection->database()->Put( 668 host_transaction_id, 669 params.object_store_id, 670 &value, 671 &scoped_handles, 672 make_scoped_ptr(new IndexedDBKey(params.key)), 673 static_cast<IndexedDBDatabase::PutMode>(params.put_mode), 674 callbacks, 675 params.index_keys); 676 TransactionIDToSizeMap* map = 677 &parent_->database_dispatcher_host_->transaction_size_map_; 678 // Size can't be big enough to overflow because it represents the 679 // actual bytes passed through IPC. 680 (*map)[host_transaction_id] += params.value.size(); 681 } 682 683 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys( 684 const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params) { 685 DCHECK( 686 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 687 IndexedDBConnection* connection = 688 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 689 if (!connection || !connection->IsConnected()) 690 return; 691 692 int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id); 693 connection->database()->SetIndexKeys( 694 host_transaction_id, 695 params.object_store_id, 696 make_scoped_ptr(new IndexedDBKey(params.primary_key)), 697 params.index_keys); 698 } 699 700 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady( 701 int32 ipc_database_id, 702 int64 transaction_id, 703 int64 object_store_id, 704 const std::vector<int64>& index_ids) { 705 DCHECK( 706 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 707 IndexedDBConnection* connection = 708 parent_->GetOrTerminateProcess(&map_, ipc_database_id); 709 if (!connection || !connection->IsConnected()) 710 return; 711 712 connection->database()->SetIndexesReady( 713 parent_->HostTransactionId(transaction_id), object_store_id, index_ids); 714 } 715 716 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpenCursor( 717 const IndexedDBHostMsg_DatabaseOpenCursor_Params& params) { 718 DCHECK( 719 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 720 IndexedDBConnection* connection = 721 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 722 if (!connection || !connection->IsConnected()) 723 return; 724 725 scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks( 726 parent_, params.ipc_thread_id, params.ipc_callbacks_id, -1)); 727 connection->database()->OpenCursor( 728 parent_->HostTransactionId(params.transaction_id), 729 params.object_store_id, 730 params.index_id, 731 make_scoped_ptr(new IndexedDBKeyRange(params.key_range)), 732 static_cast<indexed_db::CursorDirection>(params.direction), 733 params.key_only, 734 static_cast<IndexedDBDatabase::TaskType>(params.task_type), 735 callbacks); 736 } 737 738 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCount( 739 const IndexedDBHostMsg_DatabaseCount_Params& params) { 740 DCHECK( 741 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 742 IndexedDBConnection* connection = 743 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 744 if (!connection || !connection->IsConnected()) 745 return; 746 747 scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks( 748 parent_, params.ipc_thread_id, params.ipc_callbacks_id)); 749 connection->database()->Count( 750 parent_->HostTransactionId(params.transaction_id), 751 params.object_store_id, 752 params.index_id, 753 make_scoped_ptr(new IndexedDBKeyRange(params.key_range)), 754 callbacks); 755 } 756 757 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteRange( 758 const IndexedDBHostMsg_DatabaseDeleteRange_Params& params) { 759 DCHECK( 760 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 761 IndexedDBConnection* connection = 762 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 763 if (!connection || !connection->IsConnected()) 764 return; 765 766 scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks( 767 parent_, params.ipc_thread_id, params.ipc_callbacks_id)); 768 connection->database()->DeleteRange( 769 parent_->HostTransactionId(params.transaction_id), 770 params.object_store_id, 771 make_scoped_ptr(new IndexedDBKeyRange(params.key_range)), 772 callbacks); 773 } 774 775 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear( 776 int32 ipc_thread_id, 777 int32 ipc_callbacks_id, 778 int32 ipc_database_id, 779 int64 transaction_id, 780 int64 object_store_id) { 781 DCHECK( 782 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 783 IndexedDBConnection* connection = 784 parent_->GetOrTerminateProcess(&map_, ipc_database_id); 785 if (!connection || !connection->IsConnected()) 786 return; 787 788 scoped_refptr<IndexedDBCallbacks> callbacks( 789 new IndexedDBCallbacks(parent_, ipc_thread_id, ipc_callbacks_id)); 790 791 connection->database()->Clear( 792 parent_->HostTransactionId(transaction_id), object_store_id, callbacks); 793 } 794 795 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnAbort( 796 int32 ipc_database_id, 797 int64 transaction_id) { 798 DCHECK( 799 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 800 IndexedDBConnection* connection = 801 parent_->GetOrTerminateProcess(&map_, ipc_database_id); 802 if (!connection || !connection->IsConnected()) 803 return; 804 805 connection->database()->Abort(parent_->HostTransactionId(transaction_id)); 806 } 807 808 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit( 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 int64 host_transaction_id = parent_->HostTransactionId(transaction_id); 819 int64 transaction_size = transaction_size_map_[host_transaction_id]; 820 if (transaction_size && 821 parent_->Context()->WouldBeOverQuota( 822 transaction_url_map_[host_transaction_id], transaction_size)) { 823 connection->database()->Abort( 824 host_transaction_id, 825 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError)); 826 return; 827 } 828 829 connection->database()->Commit(host_transaction_id); 830 } 831 832 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateIndex( 833 const IndexedDBHostMsg_DatabaseCreateIndex_Params& params) { 834 DCHECK( 835 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 836 IndexedDBConnection* connection = 837 parent_->GetOrTerminateProcess(&map_, params.ipc_database_id); 838 if (!connection || !connection->IsConnected()) 839 return; 840 841 int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id); 842 connection->database()->CreateIndex(host_transaction_id, 843 params.object_store_id, 844 params.index_id, 845 params.name, 846 params.key_path, 847 params.unique, 848 params.multi_entry); 849 if (parent_->Context()->IsOverQuota( 850 database_url_map_[params.ipc_database_id])) { 851 connection->database()->Abort( 852 host_transaction_id, 853 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError)); 854 } 855 } 856 857 void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteIndex( 858 int32 ipc_database_id, 859 int64 transaction_id, 860 int64 object_store_id, 861 int64 index_id) { 862 DCHECK( 863 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 864 IndexedDBConnection* connection = 865 parent_->GetOrTerminateProcess(&map_, ipc_database_id); 866 if (!connection || !connection->IsConnected()) 867 return; 868 869 connection->database()->DeleteIndex( 870 parent_->HostTransactionId(transaction_id), object_store_id, index_id); 871 } 872 873 ////////////////////////////////////////////////////////////////////// 874 // IndexedDBDispatcherHost::CursorDispatcherHost 875 // 876 877 IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost( 878 IndexedDBDispatcherHost* parent) 879 : parent_(parent) { 880 map_.set_check_on_null_data(true); 881 } 882 883 IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {} 884 885 bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived( 886 const IPC::Message& message) { 887 bool handled = true; 888 IPC_BEGIN_MESSAGE_MAP( 889 IndexedDBDispatcherHost::CursorDispatcherHost, message) 890 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance) 891 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue) 892 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch) 893 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset) 894 IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed) 895 IPC_MESSAGE_UNHANDLED(handled = false) 896 IPC_END_MESSAGE_MAP() 897 898 DCHECK( 899 !handled || 900 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 901 902 return handled; 903 } 904 905 void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance( 906 int32 ipc_cursor_id, 907 int32 ipc_thread_id, 908 int32 ipc_callbacks_id, 909 uint32 count) { 910 DCHECK( 911 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 912 IndexedDBCursor* idb_cursor = 913 parent_->GetOrTerminateProcess(&map_, ipc_cursor_id); 914 if (!idb_cursor) 915 return; 916 917 idb_cursor->Advance( 918 count, 919 new IndexedDBCallbacks( 920 parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id)); 921 } 922 923 void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue( 924 int32 ipc_cursor_id, 925 int32 ipc_thread_id, 926 int32 ipc_callbacks_id, 927 const IndexedDBKey& key, 928 const IndexedDBKey& primary_key) { 929 DCHECK( 930 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 931 IndexedDBCursor* idb_cursor = 932 parent_->GetOrTerminateProcess(&map_, ipc_cursor_id); 933 if (!idb_cursor) 934 return; 935 936 idb_cursor->Continue( 937 key.IsValid() ? make_scoped_ptr(new IndexedDBKey(key)) 938 : scoped_ptr<IndexedDBKey>(), 939 primary_key.IsValid() ? make_scoped_ptr(new IndexedDBKey(primary_key)) 940 : scoped_ptr<IndexedDBKey>(), 941 new IndexedDBCallbacks( 942 parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id)); 943 } 944 945 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch( 946 int32 ipc_cursor_id, 947 int32 ipc_thread_id, 948 int32 ipc_callbacks_id, 949 int n) { 950 DCHECK( 951 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 952 IndexedDBCursor* idb_cursor = 953 parent_->GetOrTerminateProcess(&map_, ipc_cursor_id); 954 if (!idb_cursor) 955 return; 956 957 idb_cursor->PrefetchContinue( 958 n, 959 new IndexedDBCallbacks( 960 parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id)); 961 } 962 963 void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset( 964 int32 ipc_cursor_id, 965 int used_prefetches, 966 int unused_prefetches) { 967 DCHECK( 968 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 969 IndexedDBCursor* idb_cursor = 970 parent_->GetOrTerminateProcess(&map_, ipc_cursor_id); 971 if (!idb_cursor) 972 return; 973 974 leveldb::Status s = 975 idb_cursor->PrefetchReset(used_prefetches, unused_prefetches); 976 // TODO(cmumford): Handle this error (crbug.com/363397) 977 if (!s.ok()) 978 DLOG(ERROR) << "Unable to reset prefetch"; 979 } 980 981 void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed( 982 int32 ipc_object_id) { 983 DCHECK( 984 parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread()); 985 parent_->DestroyObject(&map_, ipc_object_id); 986 } 987 988 } // namespace content 989