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 35 using storage::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_.get()); 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_.get()); 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<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 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 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 291 void 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 325 void 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. 343 void IndexedDBDispatcherHost::OnPutHelper( 344 const IndexedDBHostMsg_DatabasePut_Params& params, 345 std::vector<storage::BlobDataHandle*> handles) { 346 database_dispatcher_host_->OnPut(params, handles); 347 } 348 349 void 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 357 void 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 379 template <typename ObjectType> 380 ObjectType* 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 394 template <typename ObjectType> 395 ObjectType* 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 409 template <typename MapType> 410 void 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 419 IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost( 420 IndexedDBDispatcherHost* parent) 421 : parent_(parent) { 422 map_.set_check_on_null_data(true); 423 } 424 425 IndexedDBDispatcherHost::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 431 void 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 463 bool 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 502 void 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 525 void 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 540 void 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 564 void 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 575 void 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 586 void 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 599 void 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 619 void 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 634 void 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 696 void 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 713 void 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 729 void 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 751 void 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 770 void 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 788 void 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 808 void 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 821 void 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 845 void 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 870 void 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 890 IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost( 891 IndexedDBDispatcherHost* parent) 892 : parent_(parent) { 893 map_.set_check_on_null_data(true); 894 } 895 896 IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {} 897 898 bool 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 918 void 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 936 void 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 958 void 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 976 void 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 994 void 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