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