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