1 // Copyright (c) 2013 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_database.h" 6 7 #include <math.h> 8 #include <set> 9 10 #include "base/auto_reset.h" 11 #include "base/logging.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/memory/scoped_vector.h" 14 #include "base/stl_util.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "content/browser/indexed_db/indexed_db_blob_info.h" 18 #include "content/browser/indexed_db/indexed_db_connection.h" 19 #include "content/browser/indexed_db/indexed_db_context_impl.h" 20 #include "content/browser/indexed_db/indexed_db_cursor.h" 21 #include "content/browser/indexed_db/indexed_db_factory.h" 22 #include "content/browser/indexed_db/indexed_db_index_writer.h" 23 #include "content/browser/indexed_db/indexed_db_pending_connection.h" 24 #include "content/browser/indexed_db/indexed_db_tracing.h" 25 #include "content/browser/indexed_db/indexed_db_transaction.h" 26 #include "content/browser/indexed_db/indexed_db_value.h" 27 #include "content/common/indexed_db/indexed_db_key_path.h" 28 #include "content/common/indexed_db/indexed_db_key_range.h" 29 #include "storage/browser/blob/blob_data_handle.h" 30 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" 31 #include "third_party/leveldatabase/env_chromium.h" 32 33 using base::ASCIIToUTF16; 34 using base::Int64ToString16; 35 using blink::WebIDBKeyTypeNumber; 36 37 namespace content { 38 39 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the 40 // in-progress connection. 41 class IndexedDBDatabase::PendingUpgradeCall { 42 public: 43 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks, 44 scoped_ptr<IndexedDBConnection> connection, 45 int64 transaction_id, 46 int64 version) 47 : callbacks_(callbacks), 48 connection_(connection.Pass()), 49 version_(version), 50 transaction_id_(transaction_id) {} 51 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; } 52 // Takes ownership of the connection object. 53 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT { 54 return connection_.Pass(); 55 } 56 int64 version() const { return version_; } 57 int64 transaction_id() const { return transaction_id_; } 58 59 private: 60 scoped_refptr<IndexedDBCallbacks> callbacks_; 61 scoped_ptr<IndexedDBConnection> connection_; 62 int64 version_; 63 const int64 transaction_id_; 64 }; 65 66 // PendingSuccessCall has a IndexedDBConnection* because the connection is now 67 // owned elsewhere, but we need to cancel the success call if that connection 68 // closes before it is sent. 69 class IndexedDBDatabase::PendingSuccessCall { 70 public: 71 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks, 72 IndexedDBConnection* connection, 73 int64 version) 74 : callbacks_(callbacks), connection_(connection), version_(version) {} 75 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; } 76 IndexedDBConnection* connection() const { return connection_; } 77 int64 version() const { return version_; } 78 79 private: 80 scoped_refptr<IndexedDBCallbacks> callbacks_; 81 IndexedDBConnection* connection_; 82 int64 version_; 83 }; 84 85 class IndexedDBDatabase::PendingDeleteCall { 86 public: 87 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks) 88 : callbacks_(callbacks) {} 89 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; } 90 91 private: 92 scoped_refptr<IndexedDBCallbacks> callbacks_; 93 }; 94 95 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create( 96 const base::string16& name, 97 IndexedDBBackingStore* backing_store, 98 IndexedDBFactory* factory, 99 const Identifier& unique_identifier, 100 leveldb::Status* s) { 101 scoped_refptr<IndexedDBDatabase> database = 102 new IndexedDBDatabase(name, backing_store, factory, unique_identifier); 103 *s = database->OpenInternal(); 104 if (s->ok()) 105 return database; 106 else 107 return NULL; 108 } 109 110 namespace { 111 const base::string16::value_type kNoStringVersion[] = {0}; 112 } 113 114 IndexedDBDatabase::IndexedDBDatabase(const base::string16& name, 115 IndexedDBBackingStore* backing_store, 116 IndexedDBFactory* factory, 117 const Identifier& unique_identifier) 118 : backing_store_(backing_store), 119 metadata_(name, 120 kInvalidId, 121 kNoStringVersion, 122 IndexedDBDatabaseMetadata::NO_INT_VERSION, 123 kInvalidId), 124 identifier_(unique_identifier), 125 factory_(factory) { 126 DCHECK(factory != NULL); 127 } 128 129 void IndexedDBDatabase::AddObjectStore( 130 const IndexedDBObjectStoreMetadata& object_store, 131 int64 new_max_object_store_id) { 132 DCHECK(metadata_.object_stores.find(object_store.id) == 133 metadata_.object_stores.end()); 134 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) { 135 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id); 136 metadata_.max_object_store_id = new_max_object_store_id; 137 } 138 metadata_.object_stores[object_store.id] = object_store; 139 } 140 141 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) { 142 DCHECK(metadata_.object_stores.find(object_store_id) != 143 metadata_.object_stores.end()); 144 metadata_.object_stores.erase(object_store_id); 145 } 146 147 void IndexedDBDatabase::AddIndex(int64 object_store_id, 148 const IndexedDBIndexMetadata& index, 149 int64 new_max_index_id) { 150 DCHECK(metadata_.object_stores.find(object_store_id) != 151 metadata_.object_stores.end()); 152 IndexedDBObjectStoreMetadata object_store = 153 metadata_.object_stores[object_store_id]; 154 155 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end()); 156 object_store.indexes[index.id] = index; 157 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) { 158 DCHECK_LT(object_store.max_index_id, new_max_index_id); 159 object_store.max_index_id = new_max_index_id; 160 } 161 metadata_.object_stores[object_store_id] = object_store; 162 } 163 164 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) { 165 DCHECK(metadata_.object_stores.find(object_store_id) != 166 metadata_.object_stores.end()); 167 IndexedDBObjectStoreMetadata object_store = 168 metadata_.object_stores[object_store_id]; 169 170 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end()); 171 object_store.indexes.erase(index_id); 172 metadata_.object_stores[object_store_id] = object_store; 173 } 174 175 leveldb::Status IndexedDBDatabase::OpenInternal() { 176 bool success = false; 177 leveldb::Status s = backing_store_->GetIDBDatabaseMetaData( 178 metadata_.name, &metadata_, &success); 179 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success 180 << " id = " << metadata_.id; 181 if (!s.ok()) 182 return s; 183 if (success) 184 return backing_store_->GetObjectStores(metadata_.id, 185 &metadata_.object_stores); 186 187 return backing_store_->CreateIDBDatabaseMetaData( 188 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id); 189 } 190 191 IndexedDBDatabase::~IndexedDBDatabase() { 192 DCHECK(transactions_.empty()); 193 DCHECK(pending_open_calls_.empty()); 194 DCHECK(pending_delete_calls_.empty()); 195 } 196 197 scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection( 198 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks, 199 int child_process_id) { 200 scoped_ptr<IndexedDBConnection> connection( 201 new IndexedDBConnection(this, database_callbacks)); 202 connections_.insert(connection.get()); 203 backing_store_->GrantChildProcessPermissions(child_process_id); 204 return connection.Pass(); 205 } 206 207 IndexedDBTransaction* IndexedDBDatabase::GetTransaction( 208 int64 transaction_id) const { 209 TransactionMap::const_iterator trans_iterator = 210 transactions_.find(transaction_id); 211 if (trans_iterator == transactions_.end()) 212 return NULL; 213 return trans_iterator->second; 214 } 215 216 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const { 217 if (!ContainsKey(metadata_.object_stores, object_store_id)) { 218 DLOG(ERROR) << "Invalid object_store_id"; 219 return false; 220 } 221 return true; 222 } 223 224 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id, 225 int64 index_id) const { 226 if (!ValidateObjectStoreId(object_store_id)) 227 return false; 228 const IndexedDBObjectStoreMetadata& object_store_metadata = 229 metadata_.object_stores.find(object_store_id)->second; 230 if (!ContainsKey(object_store_metadata.indexes, index_id)) { 231 DLOG(ERROR) << "Invalid index_id"; 232 return false; 233 } 234 return true; 235 } 236 237 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId( 238 int64 object_store_id, 239 int64 index_id) const { 240 if (!ValidateObjectStoreId(object_store_id)) 241 return false; 242 const IndexedDBObjectStoreMetadata& object_store_metadata = 243 metadata_.object_stores.find(object_store_id)->second; 244 if (index_id != IndexedDBIndexMetadata::kInvalidId && 245 !ContainsKey(object_store_metadata.indexes, index_id)) { 246 DLOG(ERROR) << "Invalid index_id"; 247 return false; 248 } 249 return true; 250 } 251 252 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId( 253 int64 object_store_id, 254 int64 index_id) const { 255 if (!ValidateObjectStoreId(object_store_id)) 256 return false; 257 const IndexedDBObjectStoreMetadata& object_store_metadata = 258 metadata_.object_stores.find(object_store_id)->second; 259 if (ContainsKey(object_store_metadata.indexes, index_id)) { 260 DLOG(ERROR) << "Invalid index_id"; 261 return false; 262 } 263 return true; 264 } 265 266 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id, 267 int64 object_store_id, 268 const base::string16& name, 269 const IndexedDBKeyPath& key_path, 270 bool auto_increment) { 271 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id); 272 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 273 if (!transaction) 274 return; 275 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange); 276 277 if (ContainsKey(metadata_.object_stores, object_store_id)) { 278 DLOG(ERROR) << "Invalid object_store_id"; 279 return; 280 } 281 282 // Store creation is done synchronously, as it may be followed by 283 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys 284 // may follow. 285 IndexedDBObjectStoreMetadata object_store_metadata( 286 name, 287 object_store_id, 288 key_path, 289 auto_increment, 290 IndexedDBDatabase::kMinimumIndexId); 291 292 leveldb::Status s = 293 backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(), 294 transaction->database()->id(), 295 object_store_metadata.id, 296 object_store_metadata.name, 297 object_store_metadata.key_path, 298 object_store_metadata.auto_increment); 299 if (!s.ok()) { 300 IndexedDBDatabaseError error( 301 blink::WebIDBDatabaseExceptionUnknownError, 302 ASCIIToUTF16("Internal error creating object store '") + 303 object_store_metadata.name + ASCIIToUTF16("'.")); 304 transaction->Abort(error); 305 if (leveldb_env::IsCorruption(s)) 306 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 307 error); 308 return; 309 } 310 311 AddObjectStore(object_store_metadata, object_store_id); 312 transaction->ScheduleAbortTask( 313 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation, 314 this, 315 object_store_id)); 316 } 317 318 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id, 319 int64 object_store_id) { 320 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id); 321 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 322 if (!transaction) 323 return; 324 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange); 325 326 if (!ValidateObjectStoreId(object_store_id)) 327 return; 328 329 transaction->ScheduleTask( 330 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation, 331 this, 332 object_store_id)); 333 } 334 335 void IndexedDBDatabase::CreateIndex(int64 transaction_id, 336 int64 object_store_id, 337 int64 index_id, 338 const base::string16& name, 339 const IndexedDBKeyPath& key_path, 340 bool unique, 341 bool multi_entry) { 342 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id); 343 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 344 if (!transaction) 345 return; 346 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange); 347 348 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id)) 349 return; 350 351 // Index creation is done synchronously since preemptive 352 // OpenCursor/SetIndexKeys may follow. 353 const IndexedDBIndexMetadata index_metadata( 354 name, index_id, key_path, unique, multi_entry); 355 356 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(), 357 transaction->database()->id(), 358 object_store_id, 359 index_metadata.id, 360 index_metadata.name, 361 index_metadata.key_path, 362 index_metadata.unique, 363 index_metadata.multi_entry).ok()) { 364 base::string16 error_string = 365 ASCIIToUTF16("Internal error creating index '") + 366 index_metadata.name + ASCIIToUTF16("'."); 367 transaction->Abort(IndexedDBDatabaseError( 368 blink::WebIDBDatabaseExceptionUnknownError, error_string)); 369 return; 370 } 371 372 AddIndex(object_store_id, index_metadata, index_id); 373 transaction->ScheduleAbortTask( 374 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation, 375 this, 376 object_store_id, 377 index_id)); 378 } 379 380 void IndexedDBDatabase::CreateIndexAbortOperation( 381 int64 object_store_id, 382 int64 index_id, 383 IndexedDBTransaction* transaction) { 384 IDB_TRACE1("IndexedDBDatabase::CreateIndexAbortOperation", 385 "txn.id", 386 transaction->id()); 387 DCHECK(!transaction); 388 RemoveIndex(object_store_id, index_id); 389 } 390 391 void IndexedDBDatabase::DeleteIndex(int64 transaction_id, 392 int64 object_store_id, 393 int64 index_id) { 394 IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id); 395 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 396 if (!transaction) 397 return; 398 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange); 399 400 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id)) 401 return; 402 403 transaction->ScheduleTask( 404 base::Bind(&IndexedDBDatabase::DeleteIndexOperation, 405 this, 406 object_store_id, 407 index_id)); 408 } 409 410 void IndexedDBDatabase::DeleteIndexOperation( 411 int64 object_store_id, 412 int64 index_id, 413 IndexedDBTransaction* transaction) { 414 IDB_TRACE1( 415 "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction->id()); 416 417 const IndexedDBIndexMetadata index_metadata = 418 metadata_.object_stores[object_store_id].indexes[index_id]; 419 420 leveldb::Status s = 421 backing_store_->DeleteIndex(transaction->BackingStoreTransaction(), 422 transaction->database()->id(), 423 object_store_id, 424 index_id); 425 if (!s.ok()) { 426 base::string16 error_string = 427 ASCIIToUTF16("Internal error deleting index '") + 428 index_metadata.name + ASCIIToUTF16("'."); 429 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 430 error_string); 431 transaction->Abort(error); 432 if (leveldb_env::IsCorruption(s)) 433 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 434 error); 435 return; 436 } 437 438 RemoveIndex(object_store_id, index_id); 439 transaction->ScheduleAbortTask( 440 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation, 441 this, 442 object_store_id, 443 index_metadata)); 444 } 445 446 void IndexedDBDatabase::DeleteIndexAbortOperation( 447 int64 object_store_id, 448 const IndexedDBIndexMetadata& index_metadata, 449 IndexedDBTransaction* transaction) { 450 DCHECK(!transaction); 451 IDB_TRACE1("IndexedDBDatabase::DeleteIndexAbortOperation", 452 "txn.id", 453 transaction->id()); 454 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId); 455 } 456 457 void IndexedDBDatabase::Commit(int64 transaction_id) { 458 // The frontend suggests that we commit, but we may have previously initiated 459 // an abort, and so have disposed of the transaction. on_abort has already 460 // been dispatched to the frontend, so it will find out about that 461 // asynchronously. 462 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 463 if (transaction) { 464 scoped_refptr<IndexedDBFactory> factory = factory_; 465 leveldb::Status s = transaction->Commit(); 466 if (s.IsCorruption()) { 467 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 468 "Internal error committing transaction."); 469 factory->HandleBackingStoreCorruption(identifier_.first, error); 470 } 471 } 472 } 473 474 void IndexedDBDatabase::Abort(int64 transaction_id) { 475 // If the transaction is unknown, then it has already been aborted by the 476 // backend before this call so it is safe to ignore it. 477 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id); 478 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 479 if (transaction) 480 transaction->Abort(); 481 } 482 483 void IndexedDBDatabase::Abort(int64 transaction_id, 484 const IndexedDBDatabaseError& error) { 485 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id); 486 // If the transaction is unknown, then it has already been aborted by the 487 // backend before this call so it is safe to ignore it. 488 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 489 if (transaction) 490 transaction->Abort(error); 491 } 492 493 void IndexedDBDatabase::Get(int64 transaction_id, 494 int64 object_store_id, 495 int64 index_id, 496 scoped_ptr<IndexedDBKeyRange> key_range, 497 bool key_only, 498 scoped_refptr<IndexedDBCallbacks> callbacks) { 499 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id); 500 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 501 if (!transaction) 502 return; 503 504 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 505 return; 506 507 transaction->ScheduleTask(base::Bind( 508 &IndexedDBDatabase::GetOperation, 509 this, 510 object_store_id, 511 index_id, 512 Passed(&key_range), 513 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE, 514 callbacks)); 515 } 516 517 void IndexedDBDatabase::GetOperation( 518 int64 object_store_id, 519 int64 index_id, 520 scoped_ptr<IndexedDBKeyRange> key_range, 521 indexed_db::CursorType cursor_type, 522 scoped_refptr<IndexedDBCallbacks> callbacks, 523 IndexedDBTransaction* transaction) { 524 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction->id()); 525 526 DCHECK(metadata_.object_stores.find(object_store_id) != 527 metadata_.object_stores.end()); 528 const IndexedDBObjectStoreMetadata& object_store_metadata = 529 metadata_.object_stores[object_store_id]; 530 531 const IndexedDBKey* key; 532 533 leveldb::Status s; 534 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 535 if (key_range->IsOnlyKey()) { 536 key = &key_range->lower(); 537 } else { 538 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 539 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY); 540 // ObjectStore Retrieval Operation 541 backing_store_cursor = backing_store_->OpenObjectStoreCursor( 542 transaction->BackingStoreTransaction(), 543 id(), 544 object_store_id, 545 *key_range, 546 blink::WebIDBCursorDirectionNext, 547 &s); 548 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { 549 // Index Value Retrieval Operation 550 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 551 transaction->BackingStoreTransaction(), 552 id(), 553 object_store_id, 554 index_id, 555 *key_range, 556 blink::WebIDBCursorDirectionNext, 557 &s); 558 } else { 559 // Index Referenced Value Retrieval Operation 560 backing_store_cursor = backing_store_->OpenIndexCursor( 561 transaction->BackingStoreTransaction(), 562 id(), 563 object_store_id, 564 index_id, 565 *key_range, 566 blink::WebIDBCursorDirectionNext, 567 &s); 568 } 569 570 if (!s.ok()) { 571 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString(); 572 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 573 "Internal error deleting data in range"); 574 if (leveldb_env::IsCorruption(s)) { 575 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 576 error); 577 } 578 } 579 580 if (!backing_store_cursor) { 581 callbacks->OnSuccess(); 582 return; 583 } 584 585 key = &backing_store_cursor->key(); 586 } 587 588 scoped_ptr<IndexedDBKey> primary_key; 589 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 590 // Object Store Retrieval Operation 591 IndexedDBValue value; 592 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(), 593 id(), 594 object_store_id, 595 *key, 596 &value); 597 if (!s.ok()) { 598 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 599 "Internal error in GetRecord."); 600 callbacks->OnError(error); 601 602 if (leveldb_env::IsCorruption(s)) 603 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 604 error); 605 return; 606 } 607 608 if (value.empty()) { 609 callbacks->OnSuccess(); 610 return; 611 } 612 613 if (object_store_metadata.auto_increment && 614 !object_store_metadata.key_path.IsNull()) { 615 callbacks->OnSuccess(&value, *key, object_store_metadata.key_path); 616 return; 617 } 618 619 callbacks->OnSuccess(&value); 620 return; 621 } 622 623 // From here we are dealing only with indexes. 624 s = backing_store_->GetPrimaryKeyViaIndex( 625 transaction->BackingStoreTransaction(), 626 id(), 627 object_store_id, 628 index_id, 629 *key, 630 &primary_key); 631 if (!s.ok()) { 632 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 633 "Internal error in GetPrimaryKeyViaIndex."); 634 callbacks->OnError(error); 635 if (leveldb_env::IsCorruption(s)) 636 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 637 error); 638 return; 639 } 640 if (!primary_key) { 641 callbacks->OnSuccess(); 642 return; 643 } 644 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { 645 // Index Value Retrieval Operation 646 callbacks->OnSuccess(*primary_key); 647 return; 648 } 649 650 // Index Referenced Value Retrieval Operation 651 IndexedDBValue value; 652 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(), 653 id(), 654 object_store_id, 655 *primary_key, 656 &value); 657 if (!s.ok()) { 658 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 659 "Internal error in GetRecord."); 660 callbacks->OnError(error); 661 if (leveldb_env::IsCorruption(s)) 662 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 663 error); 664 return; 665 } 666 667 if (value.empty()) { 668 callbacks->OnSuccess(); 669 return; 670 } 671 if (object_store_metadata.auto_increment && 672 !object_store_metadata.key_path.IsNull()) { 673 callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path); 674 return; 675 } 676 callbacks->OnSuccess(&value); 677 } 678 679 static scoped_ptr<IndexedDBKey> GenerateKey( 680 IndexedDBBackingStore* backing_store, 681 IndexedDBTransaction* transaction, 682 int64 database_id, 683 int64 object_store_id) { 684 const int64 max_generator_value = 685 9007199254740992LL; // Maximum integer storable as ECMAScript number. 686 int64 current_number; 687 leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber( 688 transaction->BackingStoreTransaction(), 689 database_id, 690 object_store_id, 691 ¤t_number); 692 if (!s.ok()) { 693 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber"; 694 return make_scoped_ptr(new IndexedDBKey()); 695 } 696 if (current_number < 0 || current_number > max_generator_value) 697 return make_scoped_ptr(new IndexedDBKey()); 698 699 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber)); 700 } 701 702 static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store, 703 IndexedDBTransaction* transaction, 704 int64 database_id, 705 int64 object_store_id, 706 const IndexedDBKey& key, 707 bool check_current) { 708 DCHECK_EQ(WebIDBKeyTypeNumber, key.type()); 709 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber( 710 transaction->BackingStoreTransaction(), 711 database_id, 712 object_store_id, 713 static_cast<int64>(floor(key.number())) + 1, 714 check_current); 715 } 716 717 struct IndexedDBDatabase::PutOperationParams { 718 PutOperationParams() {} 719 int64 object_store_id; 720 IndexedDBValue value; 721 ScopedVector<storage::BlobDataHandle> handles; 722 scoped_ptr<IndexedDBKey> key; 723 blink::WebIDBPutMode put_mode; 724 scoped_refptr<IndexedDBCallbacks> callbacks; 725 std::vector<IndexKeys> index_keys; 726 727 private: 728 DISALLOW_COPY_AND_ASSIGN(PutOperationParams); 729 }; 730 731 void IndexedDBDatabase::Put(int64 transaction_id, 732 int64 object_store_id, 733 IndexedDBValue* value, 734 ScopedVector<storage::BlobDataHandle>* handles, 735 scoped_ptr<IndexedDBKey> key, 736 blink::WebIDBPutMode put_mode, 737 scoped_refptr<IndexedDBCallbacks> callbacks, 738 const std::vector<IndexKeys>& index_keys) { 739 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id); 740 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 741 if (!transaction) 742 return; 743 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly); 744 745 if (!ValidateObjectStoreId(object_store_id)) 746 return; 747 748 DCHECK(key); 749 DCHECK(value); 750 scoped_ptr<PutOperationParams> params(new PutOperationParams()); 751 params->object_store_id = object_store_id; 752 params->value.swap(*value); 753 params->handles.swap(*handles); 754 params->key = key.Pass(); 755 params->put_mode = put_mode; 756 params->callbacks = callbacks; 757 params->index_keys = index_keys; 758 transaction->ScheduleTask(base::Bind( 759 &IndexedDBDatabase::PutOperation, this, base::Passed(¶ms))); 760 } 761 762 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params, 763 IndexedDBTransaction* transaction) { 764 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction->id()); 765 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly); 766 bool key_was_generated = false; 767 768 DCHECK(metadata_.object_stores.find(params->object_store_id) != 769 metadata_.object_stores.end()); 770 const IndexedDBObjectStoreMetadata& object_store = 771 metadata_.object_stores[params->object_store_id]; 772 DCHECK(object_store.auto_increment || params->key->IsValid()); 773 774 scoped_ptr<IndexedDBKey> key; 775 if (params->put_mode != blink::WebIDBPutModeCursorUpdate && 776 object_store.auto_increment && !params->key->IsValid()) { 777 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey( 778 backing_store_.get(), transaction, id(), params->object_store_id); 779 key_was_generated = true; 780 if (!auto_inc_key->IsValid()) { 781 params->callbacks->OnError( 782 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError, 783 "Maximum key generator value reached.")); 784 return; 785 } 786 key = auto_inc_key.Pass(); 787 } else { 788 key = params->key.Pass(); 789 } 790 791 DCHECK(key->IsValid()); 792 793 IndexedDBBackingStore::RecordIdentifier record_identifier; 794 if (params->put_mode == blink::WebIDBPutModeAddOnly) { 795 bool found = false; 796 leveldb::Status s = backing_store_->KeyExistsInObjectStore( 797 transaction->BackingStoreTransaction(), 798 id(), 799 params->object_store_id, 800 *key, 801 &record_identifier, 802 &found); 803 if (!s.ok()) { 804 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 805 "Internal error checking key existence."); 806 params->callbacks->OnError(error); 807 if (leveldb_env::IsCorruption(s)) 808 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 809 error); 810 return; 811 } 812 if (found) { 813 params->callbacks->OnError( 814 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError, 815 "Key already exists in the object store.")); 816 return; 817 } 818 } 819 820 ScopedVector<IndexWriter> index_writers; 821 base::string16 error_message; 822 bool obeys_constraints = false; 823 bool backing_store_success = MakeIndexWriters(transaction, 824 backing_store_.get(), 825 id(), 826 object_store, 827 *key, 828 key_was_generated, 829 params->index_keys, 830 &index_writers, 831 &error_message, 832 &obeys_constraints); 833 if (!backing_store_success) { 834 params->callbacks->OnError(IndexedDBDatabaseError( 835 blink::WebIDBDatabaseExceptionUnknownError, 836 "Internal error: backing store error updating index keys.")); 837 return; 838 } 839 if (!obeys_constraints) { 840 params->callbacks->OnError(IndexedDBDatabaseError( 841 blink::WebIDBDatabaseExceptionConstraintError, error_message)); 842 return; 843 } 844 845 // Before this point, don't do any mutation. After this point, rollback the 846 // transaction in case of error. 847 leveldb::Status s = 848 backing_store_->PutRecord(transaction->BackingStoreTransaction(), 849 id(), 850 params->object_store_id, 851 *key, 852 ¶ms->value, 853 ¶ms->handles, 854 &record_identifier); 855 if (!s.ok()) { 856 IndexedDBDatabaseError error( 857 blink::WebIDBDatabaseExceptionUnknownError, 858 "Internal error: backing store error performing put/add."); 859 params->callbacks->OnError(error); 860 if (leveldb_env::IsCorruption(s)) 861 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 862 error); 863 return; 864 } 865 866 for (size_t i = 0; i < index_writers.size(); ++i) { 867 IndexWriter* index_writer = index_writers[i]; 868 index_writer->WriteIndexKeys(record_identifier, 869 backing_store_.get(), 870 transaction->BackingStoreTransaction(), 871 id(), 872 params->object_store_id); 873 } 874 875 if (object_store.auto_increment && 876 params->put_mode != blink::WebIDBPutModeCursorUpdate && 877 key->type() == WebIDBKeyTypeNumber) { 878 leveldb::Status s = UpdateKeyGenerator(backing_store_.get(), 879 transaction, 880 id(), 881 params->object_store_id, 882 *key, 883 !key_was_generated); 884 if (!s.ok()) { 885 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 886 "Internal error updating key generator."); 887 params->callbacks->OnError(error); 888 if (leveldb_env::IsCorruption(s)) 889 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 890 error); 891 return; 892 } 893 } 894 895 params->callbacks->OnSuccess(*key); 896 } 897 898 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id, 899 int64 object_store_id, 900 scoped_ptr<IndexedDBKey> primary_key, 901 const std::vector<IndexKeys>& index_keys) { 902 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id); 903 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 904 if (!transaction) 905 return; 906 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange); 907 908 // TODO(alecflett): This method could be asynchronous, but we need to 909 // evaluate if it's worth the extra complexity. 910 IndexedDBBackingStore::RecordIdentifier record_identifier; 911 bool found = false; 912 leveldb::Status s = backing_store_->KeyExistsInObjectStore( 913 transaction->BackingStoreTransaction(), 914 metadata_.id, 915 object_store_id, 916 *primary_key, 917 &record_identifier, 918 &found); 919 if (!s.ok()) { 920 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 921 "Internal error setting index keys."); 922 transaction->Abort(error); 923 if (leveldb_env::IsCorruption(s)) 924 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 925 error); 926 return; 927 } 928 if (!found) { 929 transaction->Abort(IndexedDBDatabaseError( 930 blink::WebIDBDatabaseExceptionUnknownError, 931 "Internal error setting index keys for object store.")); 932 return; 933 } 934 935 ScopedVector<IndexWriter> index_writers; 936 base::string16 error_message; 937 bool obeys_constraints = false; 938 DCHECK(metadata_.object_stores.find(object_store_id) != 939 metadata_.object_stores.end()); 940 const IndexedDBObjectStoreMetadata& object_store_metadata = 941 metadata_.object_stores[object_store_id]; 942 bool backing_store_success = MakeIndexWriters(transaction, 943 backing_store_.get(), 944 id(), 945 object_store_metadata, 946 *primary_key, 947 false, 948 index_keys, 949 &index_writers, 950 &error_message, 951 &obeys_constraints); 952 if (!backing_store_success) { 953 transaction->Abort(IndexedDBDatabaseError( 954 blink::WebIDBDatabaseExceptionUnknownError, 955 "Internal error: backing store error updating index keys.")); 956 return; 957 } 958 if (!obeys_constraints) { 959 transaction->Abort(IndexedDBDatabaseError( 960 blink::WebIDBDatabaseExceptionConstraintError, error_message)); 961 return; 962 } 963 964 for (size_t i = 0; i < index_writers.size(); ++i) { 965 IndexWriter* index_writer = index_writers[i]; 966 index_writer->WriteIndexKeys(record_identifier, 967 backing_store_.get(), 968 transaction->BackingStoreTransaction(), 969 id(), 970 object_store_id); 971 } 972 } 973 974 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id, 975 int64, 976 const std::vector<int64>& index_ids) { 977 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id); 978 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 979 if (!transaction) 980 return; 981 DCHECK_EQ(transaction->mode(), blink::WebIDBTransactionModeVersionChange); 982 983 transaction->ScheduleTask( 984 blink::WebIDBTaskTypePreemptive, 985 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation, 986 this, 987 index_ids.size())); 988 } 989 990 void IndexedDBDatabase::SetIndexesReadyOperation( 991 size_t index_count, 992 IndexedDBTransaction* transaction) { 993 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation", 994 "txn.id", 995 transaction->id()); 996 for (size_t i = 0; i < index_count; ++i) 997 transaction->DidCompletePreemptiveEvent(); 998 } 999 1000 struct IndexedDBDatabase::OpenCursorOperationParams { 1001 OpenCursorOperationParams() {} 1002 int64 object_store_id; 1003 int64 index_id; 1004 scoped_ptr<IndexedDBKeyRange> key_range; 1005 blink::WebIDBCursorDirection direction; 1006 indexed_db::CursorType cursor_type; 1007 blink::WebIDBTaskType task_type; 1008 scoped_refptr<IndexedDBCallbacks> callbacks; 1009 1010 private: 1011 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams); 1012 }; 1013 1014 void IndexedDBDatabase::OpenCursor( 1015 int64 transaction_id, 1016 int64 object_store_id, 1017 int64 index_id, 1018 scoped_ptr<IndexedDBKeyRange> key_range, 1019 blink::WebIDBCursorDirection direction, 1020 bool key_only, 1021 blink::WebIDBTaskType task_type, 1022 scoped_refptr<IndexedDBCallbacks> callbacks) { 1023 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id); 1024 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1025 if (!transaction) 1026 return; 1027 1028 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 1029 return; 1030 1031 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams()); 1032 params->object_store_id = object_store_id; 1033 params->index_id = index_id; 1034 params->key_range = key_range.Pass(); 1035 params->direction = direction; 1036 params->cursor_type = 1037 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE; 1038 params->task_type = task_type; 1039 params->callbacks = callbacks; 1040 transaction->ScheduleTask(base::Bind( 1041 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(¶ms))); 1042 } 1043 1044 void IndexedDBDatabase::OpenCursorOperation( 1045 scoped_ptr<OpenCursorOperationParams> params, 1046 IndexedDBTransaction* transaction) { 1047 IDB_TRACE1( 1048 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction->id()); 1049 1050 // The frontend has begun indexing, so this pauses the transaction 1051 // until the indexing is complete. This can't happen any earlier 1052 // because we don't want to switch to early mode in case multiple 1053 // indexes are being created in a row, with Put()'s in between. 1054 if (params->task_type == blink::WebIDBTaskTypePreemptive) 1055 transaction->AddPreemptiveEvent(); 1056 1057 leveldb::Status s; 1058 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 1059 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) { 1060 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { 1061 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal); 1062 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( 1063 transaction->BackingStoreTransaction(), 1064 id(), 1065 params->object_store_id, 1066 *params->key_range, 1067 params->direction, 1068 &s); 1069 } else { 1070 backing_store_cursor = backing_store_->OpenObjectStoreCursor( 1071 transaction->BackingStoreTransaction(), 1072 id(), 1073 params->object_store_id, 1074 *params->key_range, 1075 params->direction, 1076 &s); 1077 } 1078 } else { 1079 DCHECK_EQ(params->task_type, blink::WebIDBTaskTypeNormal); 1080 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { 1081 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 1082 transaction->BackingStoreTransaction(), 1083 id(), 1084 params->object_store_id, 1085 params->index_id, 1086 *params->key_range, 1087 params->direction, 1088 &s); 1089 } else { 1090 backing_store_cursor = backing_store_->OpenIndexCursor( 1091 transaction->BackingStoreTransaction(), 1092 id(), 1093 params->object_store_id, 1094 params->index_id, 1095 *params->key_range, 1096 params->direction, 1097 &s); 1098 } 1099 } 1100 1101 if (!s.ok()) { 1102 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString(); 1103 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1104 "Internal error opening cursor operation"); 1105 if (leveldb_env::IsCorruption(s)) { 1106 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 1107 error); 1108 } 1109 } 1110 1111 if (!backing_store_cursor) { 1112 // Why is Success being called? 1113 params->callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL)); 1114 return; 1115 } 1116 1117 scoped_refptr<IndexedDBCursor> cursor = 1118 new IndexedDBCursor(backing_store_cursor.Pass(), 1119 params->cursor_type, 1120 params->task_type, 1121 transaction); 1122 params->callbacks->OnSuccess( 1123 cursor, cursor->key(), cursor->primary_key(), cursor->Value()); 1124 } 1125 1126 void IndexedDBDatabase::Count(int64 transaction_id, 1127 int64 object_store_id, 1128 int64 index_id, 1129 scoped_ptr<IndexedDBKeyRange> key_range, 1130 scoped_refptr<IndexedDBCallbacks> callbacks) { 1131 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id); 1132 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1133 if (!transaction) 1134 return; 1135 1136 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 1137 return; 1138 1139 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation, 1140 this, 1141 object_store_id, 1142 index_id, 1143 base::Passed(&key_range), 1144 callbacks)); 1145 } 1146 1147 void IndexedDBDatabase::CountOperation( 1148 int64 object_store_id, 1149 int64 index_id, 1150 scoped_ptr<IndexedDBKeyRange> key_range, 1151 scoped_refptr<IndexedDBCallbacks> callbacks, 1152 IndexedDBTransaction* transaction) { 1153 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction->id()); 1154 uint32 count = 0; 1155 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 1156 1157 leveldb::Status s; 1158 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 1159 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( 1160 transaction->BackingStoreTransaction(), 1161 id(), 1162 object_store_id, 1163 *key_range, 1164 blink::WebIDBCursorDirectionNext, 1165 &s); 1166 } else { 1167 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 1168 transaction->BackingStoreTransaction(), 1169 id(), 1170 object_store_id, 1171 index_id, 1172 *key_range, 1173 blink::WebIDBCursorDirectionNext, 1174 &s); 1175 } 1176 if (!s.ok()) { 1177 DLOG(ERROR) << "Unable perform count operation: " << s.ToString(); 1178 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1179 "Internal error performing count operation"); 1180 if (leveldb_env::IsCorruption(s)) { 1181 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 1182 error); 1183 } 1184 } 1185 if (!backing_store_cursor) { 1186 callbacks->OnSuccess(count); 1187 return; 1188 } 1189 1190 do { 1191 ++count; 1192 } while (backing_store_cursor->Continue(&s)); 1193 1194 // TODO(cmumford): Check for database corruption. 1195 1196 callbacks->OnSuccess(count); 1197 } 1198 1199 void IndexedDBDatabase::DeleteRange( 1200 int64 transaction_id, 1201 int64 object_store_id, 1202 scoped_ptr<IndexedDBKeyRange> key_range, 1203 scoped_refptr<IndexedDBCallbacks> callbacks) { 1204 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id); 1205 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1206 if (!transaction) 1207 return; 1208 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly); 1209 1210 if (!ValidateObjectStoreId(object_store_id)) 1211 return; 1212 1213 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation, 1214 this, 1215 object_store_id, 1216 base::Passed(&key_range), 1217 callbacks)); 1218 } 1219 1220 void IndexedDBDatabase::DeleteRangeOperation( 1221 int64 object_store_id, 1222 scoped_ptr<IndexedDBKeyRange> key_range, 1223 scoped_refptr<IndexedDBCallbacks> callbacks, 1224 IndexedDBTransaction* transaction) { 1225 IDB_TRACE1( 1226 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction->id()); 1227 leveldb::Status s = 1228 backing_store_->DeleteRange(transaction->BackingStoreTransaction(), 1229 id(), 1230 object_store_id, 1231 *key_range); 1232 if (!s.ok()) { 1233 base::string16 error_string = 1234 ASCIIToUTF16("Internal error deleting data in range"); 1235 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1236 error_string); 1237 transaction->Abort(error); 1238 if (leveldb_env::IsCorruption(s)) { 1239 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 1240 error); 1241 } 1242 return; 1243 } 1244 callbacks->OnSuccess(); 1245 } 1246 1247 void IndexedDBDatabase::Clear(int64 transaction_id, 1248 int64 object_store_id, 1249 scoped_refptr<IndexedDBCallbacks> callbacks) { 1250 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id); 1251 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1252 if (!transaction) 1253 return; 1254 DCHECK_NE(transaction->mode(), blink::WebIDBTransactionModeReadOnly); 1255 1256 if (!ValidateObjectStoreId(object_store_id)) 1257 return; 1258 1259 transaction->ScheduleTask(base::Bind( 1260 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks)); 1261 } 1262 1263 void IndexedDBDatabase::ClearOperation( 1264 int64 object_store_id, 1265 scoped_refptr<IndexedDBCallbacks> callbacks, 1266 IndexedDBTransaction* transaction) { 1267 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction->id()); 1268 leveldb::Status s = backing_store_->ClearObjectStore( 1269 transaction->BackingStoreTransaction(), id(), object_store_id); 1270 if (!s.ok()) { 1271 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1272 "Internal error clearing object store"); 1273 callbacks->OnError(error); 1274 if (leveldb_env::IsCorruption(s)) { 1275 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 1276 error); 1277 } 1278 return; 1279 } 1280 callbacks->OnSuccess(); 1281 } 1282 1283 void IndexedDBDatabase::DeleteObjectStoreOperation( 1284 int64 object_store_id, 1285 IndexedDBTransaction* transaction) { 1286 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation", 1287 "txn.id", 1288 transaction->id()); 1289 1290 const IndexedDBObjectStoreMetadata object_store_metadata = 1291 metadata_.object_stores[object_store_id]; 1292 leveldb::Status s = 1293 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(), 1294 transaction->database()->id(), 1295 object_store_id); 1296 if (!s.ok()) { 1297 base::string16 error_string = 1298 ASCIIToUTF16("Internal error deleting object store '") + 1299 object_store_metadata.name + ASCIIToUTF16("'."); 1300 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1301 error_string); 1302 transaction->Abort(error); 1303 if (leveldb_env::IsCorruption(s)) 1304 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 1305 error); 1306 return; 1307 } 1308 1309 RemoveObjectStore(object_store_id); 1310 transaction->ScheduleAbortTask( 1311 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation, 1312 this, 1313 object_store_metadata)); 1314 } 1315 1316 void IndexedDBDatabase::VersionChangeOperation( 1317 int64 version, 1318 scoped_refptr<IndexedDBCallbacks> callbacks, 1319 scoped_ptr<IndexedDBConnection> connection, 1320 IndexedDBTransaction* transaction) { 1321 IDB_TRACE1( 1322 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id()); 1323 int64 old_version = metadata_.int_version; 1324 DCHECK_GT(version, old_version); 1325 1326 if (!backing_store_->UpdateIDBDatabaseIntVersion( 1327 transaction->BackingStoreTransaction(), id(), version)) { 1328 IndexedDBDatabaseError error( 1329 blink::WebIDBDatabaseExceptionUnknownError, 1330 ASCIIToUTF16( 1331 "Internal error writing data to stable storage when " 1332 "updating version.")); 1333 callbacks->OnError(error); 1334 transaction->Abort(error); 1335 return; 1336 } 1337 1338 transaction->ScheduleAbortTask( 1339 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, 1340 this, 1341 metadata_.version, 1342 metadata_.int_version)); 1343 metadata_.int_version = version; 1344 metadata_.version = kNoStringVersion; 1345 1346 DCHECK(!pending_second_half_open_); 1347 pending_second_half_open_.reset( 1348 new PendingSuccessCall(callbacks, connection.get(), version)); 1349 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata()); 1350 } 1351 1352 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction, 1353 bool committed) { 1354 DCHECK(transactions_.find(transaction->id()) != transactions_.end()); 1355 DCHECK_EQ(transactions_[transaction->id()], transaction); 1356 transactions_.erase(transaction->id()); 1357 1358 if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) { 1359 if (pending_second_half_open_) { 1360 if (committed) { 1361 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version); 1362 DCHECK(metadata_.id != kInvalidId); 1363 1364 // Connection was already minted for OnUpgradeNeeded callback. 1365 scoped_ptr<IndexedDBConnection> connection; 1366 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(), 1367 this->metadata()); 1368 } else { 1369 pending_second_half_open_->callbacks()->OnError( 1370 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError, 1371 "Version change transaction was aborted in " 1372 "upgradeneeded event handler.")); 1373 } 1374 pending_second_half_open_.reset(); 1375 } 1376 1377 // Connection queue is now unblocked. 1378 ProcessPendingCalls(); 1379 } 1380 } 1381 1382 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) { 1383 if (status.IsCorruption()) { 1384 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1385 "Error committing transaction"); 1386 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), error); 1387 } else { 1388 factory_->HandleBackingStoreFailure(backing_store_->origin_url()); 1389 } 1390 } 1391 1392 size_t IndexedDBDatabase::ConnectionCount() const { 1393 // This does not include pending open calls, as those should not block version 1394 // changes and deletes. 1395 return connections_.size(); 1396 } 1397 1398 size_t IndexedDBDatabase::PendingOpenCount() const { 1399 return pending_open_calls_.size(); 1400 } 1401 1402 size_t IndexedDBDatabase::PendingUpgradeCount() const { 1403 return pending_run_version_change_transaction_call_ ? 1 : 0; 1404 } 1405 1406 size_t IndexedDBDatabase::RunningUpgradeCount() const { 1407 return pending_second_half_open_ ? 1 : 0; 1408 } 1409 1410 size_t IndexedDBDatabase::PendingDeleteCount() const { 1411 return pending_delete_calls_.size(); 1412 } 1413 1414 void IndexedDBDatabase::ProcessPendingCalls() { 1415 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) { 1416 DCHECK(pending_run_version_change_transaction_call_->version() > 1417 metadata_.int_version); 1418 scoped_ptr<PendingUpgradeCall> pending_call = 1419 pending_run_version_change_transaction_call_.Pass(); 1420 RunVersionChangeTransactionFinal(pending_call->callbacks(), 1421 pending_call->ReleaseConnection(), 1422 pending_call->transaction_id(), 1423 pending_call->version()); 1424 DCHECK_EQ(1u, ConnectionCount()); 1425 // Fall through would be a no-op, since transaction must complete 1426 // asynchronously. 1427 DCHECK(IsDeleteDatabaseBlocked()); 1428 DCHECK(IsOpenConnectionBlocked()); 1429 return; 1430 } 1431 1432 if (!IsDeleteDatabaseBlocked()) { 1433 PendingDeleteCallList pending_delete_calls; 1434 pending_delete_calls_.swap(pending_delete_calls); 1435 while (!pending_delete_calls.empty()) { 1436 // Only the first delete call will delete the database, but each must fire 1437 // callbacks. 1438 scoped_ptr<PendingDeleteCall> pending_delete_call( 1439 pending_delete_calls.front()); 1440 pending_delete_calls.pop_front(); 1441 DeleteDatabaseFinal(pending_delete_call->callbacks()); 1442 } 1443 // delete_database_final should never re-queue calls. 1444 DCHECK(pending_delete_calls_.empty()); 1445 // Fall through when complete, as pending opens may be unblocked. 1446 } 1447 1448 if (!IsOpenConnectionBlocked()) { 1449 PendingOpenCallList pending_open_calls; 1450 pending_open_calls_.swap(pending_open_calls); 1451 while (!pending_open_calls.empty()) { 1452 OpenConnection(pending_open_calls.front()); 1453 pending_open_calls.pop_front(); 1454 } 1455 } 1456 } 1457 1458 void IndexedDBDatabase::CreateTransaction( 1459 int64 transaction_id, 1460 IndexedDBConnection* connection, 1461 const std::vector<int64>& object_store_ids, 1462 blink::WebIDBTransactionMode mode) { 1463 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id); 1464 DCHECK(connections_.count(connection)); 1465 DCHECK(transactions_.find(transaction_id) == transactions_.end()); 1466 if (transactions_.find(transaction_id) != transactions_.end()) 1467 return; 1468 1469 // The transaction will add itself to this database's coordinator, which 1470 // manages the lifetime of the object. 1471 TransactionCreated(new IndexedDBTransaction( 1472 transaction_id, 1473 connection->callbacks(), 1474 std::set<int64>(object_store_ids.begin(), object_store_ids.end()), 1475 mode, 1476 this, 1477 new IndexedDBBackingStore::Transaction(backing_store_.get()))); 1478 } 1479 1480 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) { 1481 transactions_[transaction->id()] = transaction; 1482 } 1483 1484 bool IndexedDBDatabase::IsOpenConnectionBlocked() const { 1485 return !pending_delete_calls_.empty() || 1486 transaction_coordinator_.IsRunningVersionChangeTransaction() || 1487 pending_run_version_change_transaction_call_; 1488 } 1489 1490 void IndexedDBDatabase::OpenConnection( 1491 const IndexedDBPendingConnection& connection) { 1492 DCHECK(backing_store_.get()); 1493 1494 // TODO(jsbell): Should have a priority queue so that higher version 1495 // requests are processed first. http://crbug.com/225850 1496 if (IsOpenConnectionBlocked()) { 1497 // The backing store only detects data loss when it is first opened. The 1498 // presence of existing connections means we didn't even check for data loss 1499 // so there'd better not be any. 1500 DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss()); 1501 pending_open_calls_.push_back(connection); 1502 return; 1503 } 1504 1505 if (metadata_.id == kInvalidId) { 1506 // The database was deleted then immediately re-opened; OpenInternal() 1507 // recreates it in the backing store. 1508 if (OpenInternal().ok()) { 1509 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION, 1510 metadata_.int_version); 1511 } else { 1512 base::string16 message; 1513 if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) { 1514 message = ASCIIToUTF16( 1515 "Internal error opening database with no version specified."); 1516 } else { 1517 message = 1518 ASCIIToUTF16("Internal error opening database with version ") + 1519 Int64ToString16(connection.version); 1520 } 1521 connection.callbacks->OnError(IndexedDBDatabaseError( 1522 blink::WebIDBDatabaseExceptionUnknownError, message)); 1523 return; 1524 } 1525 } 1526 1527 // We infer that the database didn't exist from its lack of either type of 1528 // version. 1529 bool is_new_database = 1530 metadata_.version == kNoStringVersion && 1531 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION; 1532 1533 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) { 1534 // For unit tests only - skip upgrade steps. Calling from script with 1535 // DEFAULT_INT_VERSION throws exception. 1536 // TODO(jsbell): DCHECK that not in unit tests. 1537 DCHECK(is_new_database); 1538 connection.callbacks->OnSuccess( 1539 CreateConnection(connection.database_callbacks, 1540 connection.child_process_id), 1541 this->metadata()); 1542 return; 1543 } 1544 1545 // We may need to change the version. 1546 int64 local_version = connection.version; 1547 if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) { 1548 if (!is_new_database) { 1549 connection.callbacks->OnSuccess( 1550 CreateConnection(connection.database_callbacks, 1551 connection.child_process_id), 1552 this->metadata()); 1553 return; 1554 } 1555 // Spec says: If no version is specified and no database exists, set 1556 // database version to 1. 1557 local_version = 1; 1558 } 1559 1560 if (local_version > metadata_.int_version) { 1561 RunVersionChangeTransaction(connection.callbacks, 1562 CreateConnection(connection.database_callbacks, 1563 connection.child_process_id), 1564 connection.transaction_id, 1565 local_version); 1566 return; 1567 } 1568 if (local_version < metadata_.int_version) { 1569 connection.callbacks->OnError(IndexedDBDatabaseError( 1570 blink::WebIDBDatabaseExceptionVersionError, 1571 ASCIIToUTF16("The requested version (") + 1572 Int64ToString16(local_version) + 1573 ASCIIToUTF16(") is less than the existing version (") + 1574 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(")."))); 1575 return; 1576 } 1577 DCHECK_EQ(local_version, metadata_.int_version); 1578 connection.callbacks->OnSuccess( 1579 CreateConnection(connection.database_callbacks, 1580 connection.child_process_id), 1581 this->metadata()); 1582 } 1583 1584 void IndexedDBDatabase::RunVersionChangeTransaction( 1585 scoped_refptr<IndexedDBCallbacks> callbacks, 1586 scoped_ptr<IndexedDBConnection> connection, 1587 int64 transaction_id, 1588 int64 requested_version) { 1589 DCHECK(callbacks.get()); 1590 DCHECK(connections_.count(connection.get())); 1591 if (ConnectionCount() > 1) { 1592 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss()); 1593 // Front end ensures the event is not fired at connections that have 1594 // close_pending set. 1595 for (ConnectionSet::const_iterator it = connections_.begin(); 1596 it != connections_.end(); 1597 ++it) { 1598 if (*it != connection.get()) { 1599 (*it)->callbacks()->OnVersionChange(metadata_.int_version, 1600 requested_version); 1601 } 1602 } 1603 // OnBlocked will be fired at the request when one of the other 1604 // connections acks that the OnVersionChange was ignored. 1605 1606 DCHECK(!pending_run_version_change_transaction_call_); 1607 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall( 1608 callbacks, connection.Pass(), transaction_id, requested_version)); 1609 return; 1610 } 1611 RunVersionChangeTransactionFinal( 1612 callbacks, connection.Pass(), transaction_id, requested_version); 1613 } 1614 1615 void IndexedDBDatabase::RunVersionChangeTransactionFinal( 1616 scoped_refptr<IndexedDBCallbacks> callbacks, 1617 scoped_ptr<IndexedDBConnection> connection, 1618 int64 transaction_id, 1619 int64 requested_version) { 1620 1621 std::vector<int64> object_store_ids; 1622 CreateTransaction(transaction_id, 1623 connection.get(), 1624 object_store_ids, 1625 blink::WebIDBTransactionModeVersionChange); 1626 1627 transactions_[transaction_id]->ScheduleTask( 1628 base::Bind(&IndexedDBDatabase::VersionChangeOperation, 1629 this, 1630 requested_version, 1631 callbacks, 1632 base::Passed(&connection))); 1633 DCHECK(!pending_second_half_open_); 1634 } 1635 1636 void IndexedDBDatabase::DeleteDatabase( 1637 scoped_refptr<IndexedDBCallbacks> callbacks) { 1638 1639 if (IsDeleteDatabaseBlocked()) { 1640 for (ConnectionSet::const_iterator it = connections_.begin(); 1641 it != connections_.end(); 1642 ++it) { 1643 // Front end ensures the event is not fired at connections that have 1644 // close_pending set. 1645 (*it)->callbacks()->OnVersionChange( 1646 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION); 1647 } 1648 // OnBlocked will be fired at the request when one of the other 1649 // connections acks that the OnVersionChange was ignored. 1650 1651 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks)); 1652 return; 1653 } 1654 DeleteDatabaseFinal(callbacks); 1655 } 1656 1657 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const { 1658 return !!ConnectionCount(); 1659 } 1660 1661 void IndexedDBDatabase::DeleteDatabaseFinal( 1662 scoped_refptr<IndexedDBCallbacks> callbacks) { 1663 DCHECK(!IsDeleteDatabaseBlocked()); 1664 DCHECK(backing_store_.get()); 1665 leveldb::Status s = backing_store_->DeleteDatabase(metadata_.name); 1666 if (!s.ok()) { 1667 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1668 "Internal error deleting database."); 1669 callbacks->OnError(error); 1670 if (s.IsCorruption()) { 1671 GURL origin_url = backing_store_->origin_url(); 1672 backing_store_ = NULL; 1673 factory_->HandleBackingStoreCorruption(origin_url, error); 1674 } 1675 return; 1676 } 1677 int64 old_version = metadata_.int_version; 1678 metadata_.version = kNoStringVersion; 1679 metadata_.id = kInvalidId; 1680 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION; 1681 metadata_.object_stores.clear(); 1682 callbacks->OnSuccess(old_version); 1683 factory_->DatabaseDeleted(identifier_); 1684 } 1685 1686 void IndexedDBDatabase::ForceClose() { 1687 // IndexedDBConnection::ForceClose() may delete this database, so hold ref. 1688 scoped_refptr<IndexedDBDatabase> protect(this); 1689 ConnectionSet::const_iterator it = connections_.begin(); 1690 while (it != connections_.end()) { 1691 IndexedDBConnection* connection = *it++; 1692 connection->ForceClose(); 1693 } 1694 DCHECK(connections_.empty()); 1695 } 1696 1697 void IndexedDBDatabase::VersionChangeIgnored() { 1698 if (pending_run_version_change_transaction_call_) 1699 pending_run_version_change_transaction_call_->callbacks()->OnBlocked( 1700 metadata_.int_version); 1701 1702 for (PendingDeleteCallList::iterator it = pending_delete_calls_.begin(); 1703 it != pending_delete_calls_.end(); 1704 ++it) { 1705 (*it)->callbacks()->OnBlocked(metadata_.int_version); 1706 } 1707 } 1708 1709 1710 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) { 1711 DCHECK(connections_.count(connection)); 1712 DCHECK(connection->IsConnected()); 1713 DCHECK(connection->database() == this); 1714 1715 IDB_TRACE("IndexedDBDatabase::Close"); 1716 // Abort outstanding transactions from the closing connection. This 1717 // can not happen if the close is requested by the connection itself 1718 // as the front-end defers the close until all transactions are 1719 // complete, but can occur on process termination or forced close. 1720 { 1721 TransactionMap transactions(transactions_); 1722 for (TransactionMap::const_iterator it = transactions.begin(), 1723 end = transactions.end(); 1724 it != end; 1725 ++it) { 1726 if (it->second->connection() == connection->callbacks()) 1727 it->second->Abort( 1728 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, 1729 "Connection is closing.")); 1730 } 1731 } 1732 1733 connections_.erase(connection); 1734 if (pending_second_half_open_ && 1735 pending_second_half_open_->connection() == connection) { 1736 pending_second_half_open_->callbacks()->OnError( 1737 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError, 1738 "The connection was closed.")); 1739 pending_second_half_open_.reset(); 1740 } 1741 1742 ProcessPendingCalls(); 1743 1744 // TODO(jsbell): Add a test for the pending_open_calls_ cases below. 1745 if (!ConnectionCount() && !pending_open_calls_.size() && 1746 !pending_delete_calls_.size()) { 1747 DCHECK(transactions_.empty()); 1748 1749 const GURL origin_url = backing_store_->origin_url(); 1750 backing_store_ = NULL; 1751 1752 factory_->ReleaseDatabase(identifier_, forced); 1753 } 1754 } 1755 1756 void IndexedDBDatabase::CreateObjectStoreAbortOperation( 1757 int64 object_store_id, 1758 IndexedDBTransaction* transaction) { 1759 DCHECK(!transaction); 1760 IDB_TRACE1("IndexedDBDatabase::CreateObjectStoreAbortOperation", 1761 "txn.id", 1762 transaction->id()); 1763 RemoveObjectStore(object_store_id); 1764 } 1765 1766 void IndexedDBDatabase::DeleteObjectStoreAbortOperation( 1767 const IndexedDBObjectStoreMetadata& object_store_metadata, 1768 IndexedDBTransaction* transaction) { 1769 DCHECK(!transaction); 1770 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreAbortOperation", 1771 "txn.id", 1772 transaction->id()); 1773 AddObjectStore(object_store_metadata, 1774 IndexedDBObjectStoreMetadata::kInvalidId); 1775 } 1776 1777 void IndexedDBDatabase::VersionChangeAbortOperation( 1778 const base::string16& previous_version, 1779 int64 previous_int_version, 1780 IndexedDBTransaction* transaction) { 1781 DCHECK(!transaction); 1782 IDB_TRACE1("IndexedDBDatabase::VersionChangeAbortOperation", 1783 "txn.id", 1784 transaction->id()); 1785 metadata_.version = previous_version; 1786 metadata_.int_version = previous_int_version; 1787 } 1788 1789 } // namespace content 1790