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