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