indexed_db_database.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 closing_connection_(false) { 151 DCHECK(!metadata_.name.empty()); 152} 153 154void IndexedDBDatabase::AddObjectStore( 155 const IndexedDBObjectStoreMetadata& object_store, 156 int64 new_max_object_store_id) { 157 DCHECK(metadata_.object_stores.find(object_store.id) == 158 metadata_.object_stores.end()); 159 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) { 160 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id); 161 metadata_.max_object_store_id = new_max_object_store_id; 162 } 163 metadata_.object_stores[object_store.id] = object_store; 164} 165 166void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) { 167 DCHECK(metadata_.object_stores.find(object_store_id) != 168 metadata_.object_stores.end()); 169 metadata_.object_stores.erase(object_store_id); 170} 171 172void IndexedDBDatabase::AddIndex(int64 object_store_id, 173 const IndexedDBIndexMetadata& index, 174 int64 new_max_index_id) { 175 DCHECK(metadata_.object_stores.find(object_store_id) != 176 metadata_.object_stores.end()); 177 IndexedDBObjectStoreMetadata object_store = 178 metadata_.object_stores[object_store_id]; 179 180 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end()); 181 object_store.indexes[index.id] = index; 182 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) { 183 DCHECK_LT(object_store.max_index_id, new_max_index_id); 184 object_store.max_index_id = new_max_index_id; 185 } 186 metadata_.object_stores[object_store_id] = object_store; 187} 188 189void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) { 190 DCHECK(metadata_.object_stores.find(object_store_id) != 191 metadata_.object_stores.end()); 192 IndexedDBObjectStoreMetadata object_store = 193 metadata_.object_stores[object_store_id]; 194 195 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end()); 196 object_store.indexes.erase(index_id); 197 metadata_.object_stores[object_store_id] = object_store; 198} 199 200bool IndexedDBDatabase::OpenInternal() { 201 bool success = false; 202 bool ok = backing_store_->GetIDBDatabaseMetaData( 203 metadata_.name, &metadata_, &success); 204 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success 205 << " id = " << metadata_.id; 206 if (!ok) 207 return false; 208 if (success) 209 return backing_store_->GetObjectStores(metadata_.id, 210 &metadata_.object_stores); 211 212 return backing_store_->CreateIDBDatabaseMetaData( 213 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id); 214} 215 216IndexedDBDatabase::~IndexedDBDatabase() { 217 DCHECK(transactions_.empty()); 218 DCHECK(pending_open_calls_.empty()); 219 DCHECK(pending_delete_calls_.empty()); 220} 221 222scoped_refptr<IndexedDBBackingStore> IndexedDBDatabase::BackingStore() const { 223 return backing_store_; 224} 225 226IndexedDBTransaction* IndexedDBDatabase::GetTransaction( 227 int64 transaction_id) const { 228 TransactionMap::const_iterator trans_iterator = 229 transactions_.find(transaction_id); 230 if (trans_iterator == transactions_.end()) 231 return NULL; 232 return trans_iterator->second; 233} 234 235bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const { 236 if (!Contains(metadata_.object_stores, object_store_id)) { 237 DLOG(ERROR) << "Invalid object_store_id"; 238 return false; 239 } 240 return true; 241} 242 243bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id, 244 int64 index_id) const { 245 if (!ValidateObjectStoreId(object_store_id)) 246 return false; 247 const IndexedDBObjectStoreMetadata& object_store_metadata = 248 metadata_.object_stores.find(object_store_id)->second; 249 if (!Contains(object_store_metadata.indexes, index_id)) { 250 DLOG(ERROR) << "Invalid index_id"; 251 return false; 252 } 253 return true; 254} 255 256bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId( 257 int64 object_store_id, 258 int64 index_id) const { 259 if (!ValidateObjectStoreId(object_store_id)) 260 return false; 261 const IndexedDBObjectStoreMetadata& object_store_metadata = 262 metadata_.object_stores.find(object_store_id)->second; 263 if (index_id != IndexedDBIndexMetadata::kInvalidId && 264 !Contains(object_store_metadata.indexes, index_id)) { 265 DLOG(ERROR) << "Invalid index_id"; 266 return false; 267 } 268 return true; 269} 270 271bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId( 272 int64 object_store_id, 273 int64 index_id) const { 274 if (!ValidateObjectStoreId(object_store_id)) 275 return false; 276 const IndexedDBObjectStoreMetadata& object_store_metadata = 277 metadata_.object_stores.find(object_store_id)->second; 278 if (Contains(object_store_metadata.indexes, index_id)) { 279 DLOG(ERROR) << "Invalid index_id"; 280 return false; 281 } 282 return true; 283} 284 285void IndexedDBDatabase::CreateObjectStore(int64 transaction_id, 286 int64 object_store_id, 287 const string16& name, 288 const IndexedDBKeyPath& key_path, 289 bool auto_increment) { 290 IDB_TRACE("IndexedDBDatabase::CreateObjectStore"); 291 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 292 if (!transaction) 293 return; 294 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 295 296 if (Contains(metadata_.object_stores, object_store_id)) { 297 DLOG(ERROR) << "Invalid object_store_id"; 298 return; 299 } 300 301 IndexedDBObjectStoreMetadata object_store_metadata( 302 name, 303 object_store_id, 304 key_path, 305 auto_increment, 306 IndexedDBDatabase::kMinimumIndexId); 307 308 transaction->ScheduleTask( 309 base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation, 310 this, 311 object_store_metadata), 312 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation, 313 this, 314 object_store_id)); 315 316 AddObjectStore(object_store_metadata, object_store_id); 317} 318 319void IndexedDBDatabase::CreateObjectStoreOperation( 320 const IndexedDBObjectStoreMetadata& object_store_metadata, 321 IndexedDBTransaction* transaction) { 322 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation"); 323 if (!backing_store_->CreateObjectStore( 324 transaction->BackingStoreTransaction(), 325 transaction->database()->id(), 326 object_store_metadata.id, 327 object_store_metadata.name, 328 object_store_metadata.key_path, 329 object_store_metadata.auto_increment)) { 330 transaction->Abort(IndexedDBDatabaseError( 331 WebKit::WebIDBDatabaseExceptionUnknownError, 332 ASCIIToUTF16("Internal error creating object store '") + 333 object_store_metadata.name + ASCIIToUTF16("'."))); 334 return; 335 } 336} 337 338void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id, 339 int64 object_store_id) { 340 IDB_TRACE("IndexedDBDatabase::DeleteObjectStore"); 341 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 342 if (!transaction) 343 return; 344 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 345 346 if (!ValidateObjectStoreId(object_store_id)) 347 return; 348 349 const IndexedDBObjectStoreMetadata& object_store_metadata = 350 metadata_.object_stores[object_store_id]; 351 352 transaction->ScheduleTask( 353 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation, 354 this, 355 object_store_metadata), 356 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation, 357 this, 358 object_store_metadata)); 359 RemoveObjectStore(object_store_id); 360} 361 362void IndexedDBDatabase::CreateIndex(int64 transaction_id, 363 int64 object_store_id, 364 int64 index_id, 365 const string16& name, 366 const IndexedDBKeyPath& key_path, 367 bool unique, 368 bool multi_entry) { 369 IDB_TRACE("IndexedDBDatabase::CreateIndex"); 370 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 371 if (!transaction) 372 return; 373 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 374 375 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id)) 376 return; 377 const IndexedDBIndexMetadata index_metadata( 378 name, index_id, key_path, unique, multi_entry); 379 380 transaction->ScheduleTask( 381 base::Bind(&IndexedDBDatabase::CreateIndexOperation, 382 this, 383 object_store_id, 384 index_metadata), 385 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation, 386 this, 387 object_store_id, 388 index_id)); 389 390 AddIndex(object_store_id, index_metadata, index_id); 391} 392 393void IndexedDBDatabase::CreateIndexOperation( 394 int64 object_store_id, 395 const IndexedDBIndexMetadata& index_metadata, 396 IndexedDBTransaction* transaction) { 397 IDB_TRACE("IndexedDBDatabase::CreateIndexOperation"); 398 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(), 399 transaction->database()->id(), 400 object_store_id, 401 index_metadata.id, 402 index_metadata.name, 403 index_metadata.key_path, 404 index_metadata.unique, 405 index_metadata.multi_entry)) { 406 string16 error_string = ASCIIToUTF16("Internal error creating index '") + 407 index_metadata.name + ASCIIToUTF16("'."); 408 transaction->Abort(IndexedDBDatabaseError( 409 WebKit::WebIDBDatabaseExceptionUnknownError, error_string)); 410 return; 411 } 412} 413 414void IndexedDBDatabase::CreateIndexAbortOperation( 415 int64 object_store_id, 416 int64 index_id, 417 IndexedDBTransaction* transaction) { 418 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation"); 419 DCHECK(!transaction); 420 RemoveIndex(object_store_id, index_id); 421} 422 423void IndexedDBDatabase::DeleteIndex(int64 transaction_id, 424 int64 object_store_id, 425 int64 index_id) { 426 IDB_TRACE("IndexedDBDatabase::DeleteIndex"); 427 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 428 if (!transaction) 429 return; 430 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 431 432 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id)) 433 return; 434 const IndexedDBIndexMetadata& index_metadata = 435 metadata_.object_stores[object_store_id].indexes[index_id]; 436 437 transaction->ScheduleTask( 438 base::Bind(&IndexedDBDatabase::DeleteIndexOperation, 439 this, 440 object_store_id, 441 index_metadata), 442 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation, 443 this, 444 object_store_id, 445 index_metadata)); 446 447 RemoveIndex(object_store_id, index_id); 448} 449 450void IndexedDBDatabase::DeleteIndexOperation( 451 int64 object_store_id, 452 const IndexedDBIndexMetadata& index_metadata, 453 IndexedDBTransaction* transaction) { 454 IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation"); 455 bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(), 456 transaction->database()->id(), 457 object_store_id, 458 index_metadata.id); 459 if (!ok) { 460 string16 error_string = ASCIIToUTF16("Internal error deleting index '") + 461 index_metadata.name + ASCIIToUTF16("'."); 462 transaction->Abort(IndexedDBDatabaseError( 463 WebKit::WebIDBDatabaseExceptionUnknownError, error_string)); 464 } 465} 466 467void IndexedDBDatabase::DeleteIndexAbortOperation( 468 int64 object_store_id, 469 const IndexedDBIndexMetadata& index_metadata, 470 IndexedDBTransaction* transaction) { 471 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation"); 472 DCHECK(!transaction); 473 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId); 474} 475 476void IndexedDBDatabase::Commit(int64 transaction_id) { 477 // The frontend suggests that we commit, but we may have previously initiated 478 // an abort, and so have disposed of the transaction. on_abort has already 479 // been dispatched to the frontend, so it will find out about that 480 // asynchronously. 481 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 482 if (transaction) 483 transaction->Commit(); 484} 485 486void IndexedDBDatabase::Abort(int64 transaction_id) { 487 // If the transaction is unknown, then it has already been aborted by the 488 // backend before this call so it is safe to ignore it. 489 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 490 if (transaction) 491 transaction->Abort(); 492} 493 494void IndexedDBDatabase::Abort(int64 transaction_id, 495 const IndexedDBDatabaseError& error) { 496 // If the transaction is unknown, then it has already been aborted by the 497 // backend before this call so it is safe to ignore it. 498 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 499 if (transaction) 500 transaction->Abort(error); 501} 502 503void IndexedDBDatabase::Get(int64 transaction_id, 504 int64 object_store_id, 505 int64 index_id, 506 scoped_ptr<IndexedDBKeyRange> key_range, 507 bool key_only, 508 scoped_refptr<IndexedDBCallbacks> callbacks) { 509 IDB_TRACE("IndexedDBDatabase::Get"); 510 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 511 if (!transaction) 512 return; 513 514 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 515 return; 516 517 transaction->ScheduleTask(base::Bind( 518 &IndexedDBDatabase::GetOperation, 519 this, 520 object_store_id, 521 index_id, 522 Passed(&key_range), 523 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE, 524 callbacks)); 525} 526 527void IndexedDBDatabase::GetOperation( 528 int64 object_store_id, 529 int64 index_id, 530 scoped_ptr<IndexedDBKeyRange> key_range, 531 indexed_db::CursorType cursor_type, 532 scoped_refptr<IndexedDBCallbacks> callbacks, 533 IndexedDBTransaction* transaction) { 534 IDB_TRACE("IndexedDBDatabase::GetOperation"); 535 536 DCHECK(metadata_.object_stores.find(object_store_id) != 537 metadata_.object_stores.end()); 538 const IndexedDBObjectStoreMetadata& object_store_metadata = 539 metadata_.object_stores[object_store_id]; 540 541 const IndexedDBKey* key; 542 543 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 544 if (key_range->IsOnlyKey()) { 545 key = &key_range->lower(); 546 } else { 547 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 548 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY); 549 // ObjectStore Retrieval Operation 550 backing_store_cursor = backing_store_->OpenObjectStoreCursor( 551 transaction->BackingStoreTransaction(), 552 id(), 553 object_store_id, 554 *key_range, 555 indexed_db::CURSOR_NEXT); 556 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { 557 // Index Value Retrieval Operation 558 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 559 transaction->BackingStoreTransaction(), 560 id(), 561 object_store_id, 562 index_id, 563 *key_range, 564 indexed_db::CURSOR_NEXT); 565 } else { 566 // Index Referenced Value Retrieval Operation 567 backing_store_cursor = backing_store_->OpenIndexCursor( 568 transaction->BackingStoreTransaction(), 569 id(), 570 object_store_id, 571 index_id, 572 *key_range, 573 indexed_db::CURSOR_NEXT); 574 } 575 576 if (!backing_store_cursor) { 577 callbacks->OnSuccess(); 578 return; 579 } 580 581 key = &backing_store_cursor->key(); 582 } 583 584 scoped_ptr<IndexedDBKey> primary_key; 585 bool ok; 586 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 587 // Object Store Retrieval Operation 588 std::string value; 589 ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(), 590 id(), 591 object_store_id, 592 *key, 593 &value); 594 if (!ok) { 595 callbacks->OnError( 596 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 597 "Internal error in GetRecord.")); 598 return; 599 } 600 601 if (value.empty()) { 602 callbacks->OnSuccess(); 603 return; 604 } 605 606 if (object_store_metadata.auto_increment && 607 !object_store_metadata.key_path.IsNull()) { 608 callbacks->OnSuccess(&value, *key, object_store_metadata.key_path); 609 return; 610 } 611 612 callbacks->OnSuccess(&value); 613 return; 614 } 615 616 // From here we are dealing only with indexes. 617 ok = backing_store_->GetPrimaryKeyViaIndex( 618 transaction->BackingStoreTransaction(), 619 id(), 620 object_store_id, 621 index_id, 622 *key, 623 &primary_key); 624 if (!ok) { 625 callbacks->OnError( 626 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 627 "Internal error in GetPrimaryKeyViaIndex.")); 628 return; 629 } 630 if (!primary_key) { 631 callbacks->OnSuccess(); 632 return; 633 } 634 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { 635 // Index Value Retrieval Operation 636 callbacks->OnSuccess(*primary_key); 637 return; 638 } 639 640 // Index Referenced Value Retrieval Operation 641 std::string value; 642 ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(), 643 id(), 644 object_store_id, 645 *primary_key, 646 &value); 647 if (!ok) { 648 callbacks->OnError( 649 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 650 "Internal error in GetRecord.")); 651 return; 652 } 653 654 if (value.empty()) { 655 callbacks->OnSuccess(); 656 return; 657 } 658 if (object_store_metadata.auto_increment && 659 !object_store_metadata.key_path.IsNull()) { 660 callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path); 661 return; 662 } 663 callbacks->OnSuccess(&value); 664} 665 666static scoped_ptr<IndexedDBKey> GenerateKey( 667 scoped_refptr<IndexedDBBackingStore> backing_store, 668 scoped_refptr<IndexedDBTransaction> transaction, 669 int64 database_id, 670 int64 object_store_id) { 671 const int64 max_generator_value = 672 9007199254740992LL; // Maximum integer storable as ECMAScript number. 673 int64 current_number; 674 bool ok = backing_store->GetKeyGeneratorCurrentNumber( 675 transaction->BackingStoreTransaction(), 676 database_id, 677 object_store_id, 678 ¤t_number); 679 if (!ok) { 680 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber"; 681 return make_scoped_ptr(new IndexedDBKey()); 682 } 683 if (current_number < 0 || current_number > max_generator_value) 684 return make_scoped_ptr(new IndexedDBKey()); 685 686 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber)); 687} 688 689static bool UpdateKeyGenerator( 690 scoped_refptr<IndexedDBBackingStore> backing_store, 691 scoped_refptr<IndexedDBTransaction> transaction, 692 int64 database_id, 693 int64 object_store_id, 694 const IndexedDBKey& key, 695 bool check_current) { 696 DCHECK_EQ(WebIDBKeyTypeNumber, key.type()); 697 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber( 698 transaction->BackingStoreTransaction(), 699 database_id, 700 object_store_id, 701 static_cast<int64>(floor(key.number())) + 1, 702 check_current); 703} 704 705struct IndexedDBDatabase::PutOperationParams { 706 PutOperationParams() {} 707 int64 object_store_id; 708 std::string value; 709 scoped_ptr<IndexedDBKey> key; 710 IndexedDBDatabase::PutMode put_mode; 711 scoped_refptr<IndexedDBCallbacks> callbacks; 712 std::vector<int64> index_ids; 713 std::vector<IndexKeys> index_keys; 714 715 DISALLOW_COPY_AND_ASSIGN(PutOperationParams); 716}; 717 718void IndexedDBDatabase::Put(int64 transaction_id, 719 int64 object_store_id, 720 std::string* value, 721 scoped_ptr<IndexedDBKey> key, 722 PutMode put_mode, 723 scoped_refptr<IndexedDBCallbacks> callbacks, 724 const std::vector<int64>& index_ids, 725 const std::vector<IndexKeys>& index_keys) { 726 IDB_TRACE("IndexedDBDatabase::Put"); 727 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 728 if (!transaction) 729 return; 730 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 731 732 if (!ValidateObjectStoreId(object_store_id)) 733 return; 734 735 DCHECK(key); 736 scoped_ptr<PutOperationParams> params(new PutOperationParams()); 737 params->object_store_id = object_store_id; 738 params->value.swap(*value); 739 params->key = key.Pass(); 740 params->put_mode = put_mode; 741 params->callbacks = callbacks; 742 params->index_ids = index_ids; 743 params->index_keys = index_keys; 744 transaction->ScheduleTask(base::Bind( 745 &IndexedDBDatabase::PutOperation, this, base::Passed(¶ms))); 746} 747 748void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params, 749 IndexedDBTransaction* transaction) { 750 IDB_TRACE("IndexedDBDatabase::PutOperation"); 751 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 752 DCHECK_EQ(params->index_ids.size(), params->index_keys.size()); 753 bool key_was_generated = false; 754 755 DCHECK(metadata_.object_stores.find(params->object_store_id) != 756 metadata_.object_stores.end()); 757 const IndexedDBObjectStoreMetadata& object_store = 758 metadata_.object_stores[params->object_store_id]; 759 DCHECK(object_store.auto_increment || params->key->IsValid()); 760 761 scoped_ptr<IndexedDBKey> key; 762 if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE && 763 object_store.auto_increment && !params->key->IsValid()) { 764 scoped_ptr<IndexedDBKey> auto_inc_key = 765 GenerateKey(backing_store_, transaction, id(), params->object_store_id); 766 key_was_generated = true; 767 if (!auto_inc_key->IsValid()) { 768 params->callbacks->OnError( 769 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError, 770 "Maximum key generator value reached.")); 771 return; 772 } 773 key = auto_inc_key.Pass(); 774 } else { 775 key = params->key.Pass(); 776 } 777 778 DCHECK(key->IsValid()); 779 780 IndexedDBBackingStore::RecordIdentifier record_identifier; 781 if (params->put_mode == IndexedDBDatabase::ADD_ONLY) { 782 bool found = false; 783 bool ok = backing_store_->KeyExistsInObjectStore( 784 transaction->BackingStoreTransaction(), 785 id(), 786 params->object_store_id, 787 *key, 788 &record_identifier, 789 &found); 790 if (!ok) { 791 params->callbacks->OnError( 792 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 793 "Internal error checking key existence.")); 794 return; 795 } 796 if (found) { 797 params->callbacks->OnError( 798 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError, 799 "Key already exists in the object store.")); 800 return; 801 } 802 } 803 804 ScopedVector<IndexWriter> index_writers; 805 string16 error_message; 806 bool obeys_constraints = false; 807 bool backing_store_success = MakeIndexWriters(transaction, 808 backing_store_.get(), 809 id(), 810 object_store, 811 *key, 812 key_was_generated, 813 params->index_ids, 814 params->index_keys, 815 &index_writers, 816 &error_message, 817 &obeys_constraints); 818 if (!backing_store_success) { 819 params->callbacks->OnError(IndexedDBDatabaseError( 820 WebKit::WebIDBDatabaseExceptionUnknownError, 821 "Internal error: backing store error updating index keys.")); 822 return; 823 } 824 if (!obeys_constraints) { 825 params->callbacks->OnError(IndexedDBDatabaseError( 826 WebKit::WebIDBDatabaseExceptionConstraintError, error_message)); 827 return; 828 } 829 830 // Before this point, don't do any mutation. After this point, rollback the 831 // transaction in case of error. 832 backing_store_success = 833 backing_store_->PutRecord(transaction->BackingStoreTransaction(), 834 id(), 835 params->object_store_id, 836 *key, 837 params->value, 838 &record_identifier); 839 if (!backing_store_success) { 840 params->callbacks->OnError(IndexedDBDatabaseError( 841 WebKit::WebIDBDatabaseExceptionUnknownError, 842 "Internal error: backing store error performing put/add.")); 843 return; 844 } 845 846 for (size_t i = 0; i < index_writers.size(); ++i) { 847 IndexWriter* index_writer = index_writers[i]; 848 index_writer->WriteIndexKeys(record_identifier, 849 backing_store_.get(), 850 transaction->BackingStoreTransaction(), 851 id(), 852 params->object_store_id); 853 } 854 855 if (object_store.auto_increment && 856 params->put_mode != IndexedDBDatabase::CURSOR_UPDATE && 857 key->type() == WebIDBKeyTypeNumber) { 858 bool ok = UpdateKeyGenerator(backing_store_, 859 transaction, 860 id(), 861 params->object_store_id, 862 *key, 863 !key_was_generated); 864 if (!ok) { 865 params->callbacks->OnError( 866 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 867 "Internal error updating key generator.")); 868 return; 869 } 870 } 871 872 params->callbacks->OnSuccess(*key); 873} 874 875void IndexedDBDatabase::SetIndexKeys(int64 transaction_id, 876 int64 object_store_id, 877 scoped_ptr<IndexedDBKey> primary_key, 878 const std::vector<int64>& index_ids, 879 const std::vector<IndexKeys>& index_keys) { 880 IDB_TRACE("IndexedDBDatabase::SetIndexKeys"); 881 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 882 if (!transaction) 883 return; 884 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 885 886 scoped_refptr<IndexedDBBackingStore> store = BackingStore(); 887 // TODO(alecflett): This method could be asynchronous, but we need to 888 // evaluate if it's worth the extra complexity. 889 IndexedDBBackingStore::RecordIdentifier record_identifier; 890 bool found = false; 891 bool ok = 892 store->KeyExistsInObjectStore(transaction->BackingStoreTransaction(), 893 metadata_.id, 894 object_store_id, 895 *primary_key, 896 &record_identifier, 897 &found); 898 if (!ok) { 899 transaction->Abort( 900 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 901 "Internal error setting index keys.")); 902 return; 903 } 904 if (!found) { 905 transaction->Abort(IndexedDBDatabaseError( 906 WebKit::WebIDBDatabaseExceptionUnknownError, 907 "Internal error setting index keys for object store.")); 908 return; 909 } 910 911 ScopedVector<IndexWriter> index_writers; 912 string16 error_message; 913 bool obeys_constraints = false; 914 DCHECK(metadata_.object_stores.find(object_store_id) != 915 metadata_.object_stores.end()); 916 const IndexedDBObjectStoreMetadata& object_store_metadata = 917 metadata_.object_stores[object_store_id]; 918 bool backing_store_success = MakeIndexWriters(transaction, 919 store, 920 id(), 921 object_store_metadata, 922 *primary_key, 923 false, 924 index_ids, 925 index_keys, 926 &index_writers, 927 &error_message, 928 &obeys_constraints); 929 if (!backing_store_success) { 930 transaction->Abort(IndexedDBDatabaseError( 931 WebKit::WebIDBDatabaseExceptionUnknownError, 932 "Internal error: backing store error updating index keys.")); 933 return; 934 } 935 if (!obeys_constraints) { 936 transaction->Abort(IndexedDBDatabaseError( 937 WebKit::WebIDBDatabaseExceptionConstraintError, error_message)); 938 return; 939 } 940 941 for (size_t i = 0; i < index_writers.size(); ++i) { 942 IndexWriter* index_writer = index_writers[i]; 943 index_writer->WriteIndexKeys(record_identifier, 944 store, 945 transaction->BackingStoreTransaction(), 946 id(), 947 object_store_id); 948 } 949} 950 951void IndexedDBDatabase::SetIndexesReady(int64 transaction_id, 952 int64, 953 const std::vector<int64>& index_ids) { 954 IDB_TRACE("IndexedDBDatabase::SetIndexesReady"); 955 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 956 if (!transaction) 957 return; 958 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 959 960 transaction->ScheduleTask( 961 IndexedDBDatabase::PREEMPTIVE_TASK, 962 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation, 963 this, 964 index_ids.size())); 965} 966 967void IndexedDBDatabase::SetIndexesReadyOperation( 968 size_t index_count, 969 IndexedDBTransaction* transaction) { 970 IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation"); 971 for (size_t i = 0; i < index_count; ++i) 972 transaction->DidCompletePreemptiveEvent(); 973} 974 975struct IndexedDBDatabase::OpenCursorOperationParams { 976 OpenCursorOperationParams() {} 977 int64 object_store_id; 978 int64 index_id; 979 scoped_ptr<IndexedDBKeyRange> key_range; 980 indexed_db::CursorDirection direction; 981 indexed_db::CursorType cursor_type; 982 IndexedDBDatabase::TaskType task_type; 983 scoped_refptr<IndexedDBCallbacks> callbacks; 984 985 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams); 986}; 987 988void IndexedDBDatabase::OpenCursor( 989 int64 transaction_id, 990 int64 object_store_id, 991 int64 index_id, 992 scoped_ptr<IndexedDBKeyRange> key_range, 993 indexed_db::CursorDirection direction, 994 bool key_only, 995 TaskType task_type, 996 scoped_refptr<IndexedDBCallbacks> callbacks) { 997 IDB_TRACE("IndexedDBDatabase::OpenCursor"); 998 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 999 if (!transaction) 1000 return; 1001 1002 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 1003 return; 1004 1005 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams()); 1006 params->object_store_id = object_store_id; 1007 params->index_id = index_id; 1008 params->key_range = key_range.Pass(); 1009 params->direction = direction; 1010 params->cursor_type = 1011 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE; 1012 params->task_type = task_type; 1013 params->callbacks = callbacks; 1014 transaction->ScheduleTask(base::Bind( 1015 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(¶ms))); 1016} 1017 1018void IndexedDBDatabase::OpenCursorOperation( 1019 scoped_ptr<OpenCursorOperationParams> params, 1020 IndexedDBTransaction* transaction) { 1021 IDB_TRACE("IndexedDBDatabase::OpenCursorOperation"); 1022 1023 // The frontend has begun indexing, so this pauses the transaction 1024 // until the indexing is complete. This can't happen any earlier 1025 // because we don't want to switch to early mode in case multiple 1026 // indexes are being created in a row, with Put()'s in between. 1027 if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK) 1028 transaction->AddPreemptiveEvent(); 1029 1030 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 1031 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) { 1032 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { 1033 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK); 1034 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( 1035 transaction->BackingStoreTransaction(), 1036 id(), 1037 params->object_store_id, 1038 *params->key_range, 1039 params->direction); 1040 } else { 1041 backing_store_cursor = backing_store_->OpenObjectStoreCursor( 1042 transaction->BackingStoreTransaction(), 1043 id(), 1044 params->object_store_id, 1045 *params->key_range, 1046 params->direction); 1047 } 1048 } else { 1049 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK); 1050 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { 1051 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 1052 transaction->BackingStoreTransaction(), 1053 id(), 1054 params->object_store_id, 1055 params->index_id, 1056 *params->key_range, 1057 params->direction); 1058 } else { 1059 backing_store_cursor = backing_store_->OpenIndexCursor( 1060 transaction->BackingStoreTransaction(), 1061 id(), 1062 params->object_store_id, 1063 params->index_id, 1064 *params->key_range, 1065 params->direction); 1066 } 1067 } 1068 1069 if (!backing_store_cursor) { 1070 params->callbacks->OnSuccess(static_cast<std::string*>(NULL)); 1071 return; 1072 } 1073 1074 scoped_refptr<IndexedDBCursor> cursor = 1075 new IndexedDBCursor(backing_store_cursor.Pass(), 1076 params->cursor_type, 1077 params->task_type, 1078 transaction); 1079 params->callbacks->OnSuccess( 1080 cursor, cursor->key(), cursor->primary_key(), cursor->Value()); 1081} 1082 1083void IndexedDBDatabase::Count(int64 transaction_id, 1084 int64 object_store_id, 1085 int64 index_id, 1086 scoped_ptr<IndexedDBKeyRange> key_range, 1087 scoped_refptr<IndexedDBCallbacks> callbacks) { 1088 IDB_TRACE("IndexedDBDatabase::Count"); 1089 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1090 if (!transaction) 1091 return; 1092 1093 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 1094 return; 1095 1096 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation, 1097 this, 1098 object_store_id, 1099 index_id, 1100 base::Passed(&key_range), 1101 callbacks)); 1102} 1103 1104void IndexedDBDatabase::CountOperation( 1105 int64 object_store_id, 1106 int64 index_id, 1107 scoped_ptr<IndexedDBKeyRange> key_range, 1108 scoped_refptr<IndexedDBCallbacks> callbacks, 1109 IndexedDBTransaction* transaction) { 1110 IDB_TRACE("IndexedDBDatabase::CountOperation"); 1111 uint32 count = 0; 1112 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 1113 1114 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 1115 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( 1116 transaction->BackingStoreTransaction(), 1117 id(), 1118 object_store_id, 1119 *key_range, 1120 indexed_db::CURSOR_NEXT); 1121 } else { 1122 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 1123 transaction->BackingStoreTransaction(), 1124 id(), 1125 object_store_id, 1126 index_id, 1127 *key_range, 1128 indexed_db::CURSOR_NEXT); 1129 } 1130 if (!backing_store_cursor) { 1131 callbacks->OnSuccess(count); 1132 return; 1133 } 1134 1135 do { 1136 ++count; 1137 } while (backing_store_cursor->Continue()); 1138 1139 callbacks->OnSuccess(count); 1140} 1141 1142void IndexedDBDatabase::DeleteRange( 1143 int64 transaction_id, 1144 int64 object_store_id, 1145 scoped_ptr<IndexedDBKeyRange> key_range, 1146 scoped_refptr<IndexedDBCallbacks> callbacks) { 1147 IDB_TRACE("IndexedDBDatabase::DeleteRange"); 1148 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1149 if (!transaction) 1150 return; 1151 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 1152 1153 if (!ValidateObjectStoreId(object_store_id)) 1154 return; 1155 1156 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation, 1157 this, 1158 object_store_id, 1159 base::Passed(&key_range), 1160 callbacks)); 1161} 1162 1163void IndexedDBDatabase::DeleteRangeOperation( 1164 int64 object_store_id, 1165 scoped_ptr<IndexedDBKeyRange> key_range, 1166 scoped_refptr<IndexedDBCallbacks> callbacks, 1167 IndexedDBTransaction* transaction) { 1168 IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation"); 1169 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor = 1170 backing_store_->OpenObjectStoreCursor( 1171 transaction->BackingStoreTransaction(), 1172 id(), 1173 object_store_id, 1174 *key_range, 1175 indexed_db::CURSOR_NEXT); 1176 if (backing_store_cursor) { 1177 do { 1178 if (!backing_store_->DeleteRecord( 1179 transaction->BackingStoreTransaction(), 1180 id(), 1181 object_store_id, 1182 backing_store_cursor->record_identifier())) { 1183 callbacks->OnError( 1184 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 1185 "Internal error deleting data in range")); 1186 return; 1187 } 1188 } while (backing_store_cursor->Continue()); 1189 } 1190 1191 callbacks->OnSuccess(); 1192} 1193 1194void IndexedDBDatabase::Clear(int64 transaction_id, 1195 int64 object_store_id, 1196 scoped_refptr<IndexedDBCallbacks> callbacks) { 1197 IDB_TRACE("IndexedDBDatabase::Clear"); 1198 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1199 if (!transaction) 1200 return; 1201 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 1202 1203 if (!ValidateObjectStoreId(object_store_id)) 1204 return; 1205 1206 transaction->ScheduleTask(base::Bind( 1207 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks)); 1208} 1209 1210void IndexedDBDatabase::ClearOperation( 1211 int64 object_store_id, 1212 scoped_refptr<IndexedDBCallbacks> callbacks, 1213 IndexedDBTransaction* transaction) { 1214 IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation"); 1215 if (!backing_store_->ClearObjectStore( 1216 transaction->BackingStoreTransaction(), id(), object_store_id)) { 1217 callbacks->OnError( 1218 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 1219 "Internal error clearing object store")); 1220 return; 1221 } 1222 callbacks->OnSuccess(); 1223} 1224 1225void IndexedDBDatabase::DeleteObjectStoreOperation( 1226 const IndexedDBObjectStoreMetadata& object_store_metadata, 1227 IndexedDBTransaction* transaction) { 1228 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation"); 1229 bool ok = 1230 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(), 1231 transaction->database()->id(), 1232 object_store_metadata.id); 1233 if (!ok) { 1234 string16 error_string = 1235 ASCIIToUTF16("Internal error deleting object store '") + 1236 object_store_metadata.name + ASCIIToUTF16("'."); 1237 transaction->Abort(IndexedDBDatabaseError( 1238 WebKit::WebIDBDatabaseExceptionUnknownError, error_string)); 1239 } 1240} 1241 1242void IndexedDBDatabase::VersionChangeOperation( 1243 int64 version, 1244 scoped_refptr<IndexedDBCallbacks> callbacks, 1245 scoped_ptr<IndexedDBConnection> connection, 1246 WebKit::WebIDBCallbacks::DataLoss data_loss, 1247 IndexedDBTransaction* transaction) { 1248 IDB_TRACE("IndexedDBDatabase::VersionChangeOperation"); 1249 int64 old_version = metadata_.int_version; 1250 DCHECK_GT(version, old_version); 1251 metadata_.int_version = version; 1252 if (!backing_store_->UpdateIDBDatabaseIntVersion( 1253 transaction->BackingStoreTransaction(), 1254 id(), 1255 metadata_.int_version)) { 1256 IndexedDBDatabaseError error( 1257 WebKit::WebIDBDatabaseExceptionUnknownError, 1258 ASCIIToUTF16( 1259 "Internal error writing data to stable storage when " 1260 "updating version.")); 1261 callbacks->OnError(error); 1262 transaction->Abort(error); 1263 return; 1264 } 1265 DCHECK(!pending_second_half_open_); 1266 pending_second_half_open_.reset(new PendingSuccessCall( 1267 callbacks, connection.get(), transaction->id(), version)); 1268 callbacks->OnUpgradeNeeded( 1269 old_version, connection.Pass(), metadata(), data_loss); 1270} 1271 1272void IndexedDBDatabase::TransactionStarted(IndexedDBTransaction* transaction) { 1273 1274 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) { 1275 DCHECK(!running_version_change_transaction_); 1276 running_version_change_transaction_ = transaction; 1277 } 1278} 1279 1280void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction) { 1281 1282 DCHECK(transactions_.find(transaction->id()) != transactions_.end()); 1283 DCHECK_EQ(transactions_[transaction->id()], transaction); 1284 transactions_.erase(transaction->id()); 1285 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) { 1286 DCHECK_EQ(transaction, running_version_change_transaction_); 1287 running_version_change_transaction_ = NULL; 1288 } 1289} 1290 1291void IndexedDBDatabase::TransactionFinishedAndAbortFired( 1292 IndexedDBTransaction* transaction) { 1293 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) { 1294 if (pending_second_half_open_) { 1295 pending_second_half_open_->Callbacks()->OnError( 1296 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError, 1297 "Version change transaction was aborted in " 1298 "upgradeneeded event handler.")); 1299 pending_second_half_open_.reset(); 1300 } 1301 ProcessPendingCalls(); 1302 } 1303} 1304 1305void IndexedDBDatabase::TransactionFinishedAndCompleteFired( 1306 IndexedDBTransaction* transaction) { 1307 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) { 1308 DCHECK(pending_second_half_open_); 1309 if (pending_second_half_open_) { 1310 DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version); 1311 DCHECK(metadata_.id != kInvalidId); 1312 1313 // Connection was already minted for OnUpgradeNeeded callback. 1314 scoped_ptr<IndexedDBConnection> connection; 1315 1316 pending_second_half_open_->Callbacks()->OnSuccess(connection.Pass(), 1317 this->metadata()); 1318 pending_second_half_open_.reset(); 1319 } 1320 ProcessPendingCalls(); 1321 } 1322} 1323 1324size_t IndexedDBDatabase::ConnectionCount() const { 1325 // This does not include pending open calls, as those should not block version 1326 // changes and deletes. 1327 return connections_.size(); 1328} 1329 1330size_t IndexedDBDatabase::PendingOpenCount() const { 1331 return pending_open_calls_.size(); 1332} 1333 1334size_t IndexedDBDatabase::PendingUpgradeCount() const { 1335 return pending_run_version_change_transaction_call_ ? 1 : 0; 1336} 1337 1338size_t IndexedDBDatabase::RunningUpgradeCount() const { 1339 return pending_second_half_open_ ? 1 : 0; 1340} 1341 1342size_t IndexedDBDatabase::PendingDeleteCount() const { 1343 return pending_delete_calls_.size(); 1344} 1345 1346void IndexedDBDatabase::ProcessPendingCalls() { 1347 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) { 1348 DCHECK(pending_run_version_change_transaction_call_->Version() > 1349 metadata_.int_version); 1350 scoped_ptr<PendingUpgradeCall> pending_call = 1351 pending_run_version_change_transaction_call_.Pass(); 1352 RunVersionChangeTransactionFinal(pending_call->Callbacks(), 1353 pending_call->Connection(), 1354 pending_call->TransactionId(), 1355 pending_call->Version()); 1356 DCHECK_EQ(static_cast<size_t>(1), ConnectionCount()); 1357 // Fall through would be a no-op, since transaction must complete 1358 // asynchronously. 1359 DCHECK(IsDeleteDatabaseBlocked()); 1360 DCHECK(IsOpenConnectionBlocked()); 1361 return; 1362 } 1363 1364 if (!IsDeleteDatabaseBlocked()) { 1365 PendingDeleteCallList pending_delete_calls; 1366 pending_delete_calls_.swap(pending_delete_calls); 1367 while (!pending_delete_calls.empty()) { 1368 // Only the first delete call will delete the database, but each must fire 1369 // callbacks. 1370 scoped_ptr<PendingDeleteCall> pending_delete_call( 1371 pending_delete_calls.front()); 1372 pending_delete_calls.pop_front(); 1373 DeleteDatabaseFinal(pending_delete_call->Callbacks()); 1374 } 1375 // delete_database_final should never re-queue calls. 1376 DCHECK(pending_delete_calls_.empty()); 1377 // Fall through when complete, as pending opens may be unblocked. 1378 } 1379 1380 if (!IsOpenConnectionBlocked()) { 1381 PendingOpenCallList pending_open_calls; 1382 pending_open_calls_.swap(pending_open_calls); 1383 while (!pending_open_calls.empty()) { 1384 scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front()); 1385 pending_open_calls.pop_front(); 1386 OpenConnection(pending_open_call->Callbacks(), 1387 pending_open_call->DatabaseCallbacks(), 1388 pending_open_call->TransactionId(), 1389 pending_open_call->Version()); 1390 } 1391 } 1392} 1393 1394void IndexedDBDatabase::CreateTransaction( 1395 int64 transaction_id, 1396 IndexedDBConnection* connection, 1397 const std::vector<int64>& object_store_ids, 1398 uint16 mode) { 1399 1400 DCHECK(connections_.has(connection)); 1401 1402 scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction( 1403 transaction_id, 1404 connection->callbacks(), 1405 std::set<int64>(object_store_ids.begin(), object_store_ids.end()), 1406 static_cast<indexed_db::TransactionMode>(mode), 1407 this); 1408 DCHECK(transactions_.find(transaction_id) == transactions_.end()); 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_.has(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_.has(connection)); 1646 1647 // Close outstanding transactions from the closing connection. This 1648 // can not happen if the close is requested by the connection itself 1649 // as the front-end defers the close until all transactions are 1650 // complete, so something unusual has happened e.g. unexpected 1651 // process termination. 1652 { 1653 TransactionMap transactions(transactions_); 1654 for (TransactionMap::const_iterator it = transactions.begin(), 1655 end = transactions.end(); 1656 it != end; 1657 ++it) { 1658 if (it->second->connection() == connection->callbacks()) 1659 it->second->Abort( 1660 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 1661 "Connection is closing.")); 1662 } 1663 } 1664 1665 connections_.erase(connection); 1666 if (pending_second_half_open_ && 1667 pending_second_half_open_->Connection() == connection) { 1668 pending_second_half_open_->Callbacks()->OnError( 1669 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError, 1670 "The connection was closed.")); 1671 pending_second_half_open_.reset(); 1672 } 1673 1674 // process_pending_calls allows the inspector to process a pending open call 1675 // and call close, reentering IndexedDBDatabase::close. Then the 1676 // backend would be removed both by the inspector closing its connection, and 1677 // by the connection that first called close. 1678 // To avoid that situation, don't proceed in case of reentrancy. 1679 if (closing_connection_) 1680 return; 1681 base::AutoReset<bool> ClosingConnection(&closing_connection_, true); 1682 ProcessPendingCalls(); 1683 1684 // TODO(jsbell): Add a test for the pending_open_calls_ cases below. 1685 if (!ConnectionCount() && !pending_open_calls_.size() && 1686 !pending_delete_calls_.size()) { 1687 DCHECK(transactions_.empty()); 1688 1689 // factory_ should only be null in unit tests. 1690 // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow. 1691 if (factory_) { 1692 DCHECK(backing_store_.get()); 1693 factory_->ReleaseDatabase(identifier_, forced); 1694 factory_ = NULL; 1695 } 1696 1697 // Drop reference to backing store after informing factory, so 1698 // that factory can do accounting on it. 1699 backing_store_ = NULL; 1700 } 1701} 1702 1703void IndexedDBDatabase::CreateObjectStoreAbortOperation( 1704 int64 object_store_id, 1705 IndexedDBTransaction* transaction) { 1706 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation"); 1707 DCHECK(!transaction); 1708 RemoveObjectStore(object_store_id); 1709} 1710 1711void IndexedDBDatabase::DeleteObjectStoreAbortOperation( 1712 const IndexedDBObjectStoreMetadata& object_store_metadata, 1713 IndexedDBTransaction* transaction) { 1714 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation"); 1715 DCHECK(!transaction); 1716 AddObjectStore(object_store_metadata, 1717 IndexedDBObjectStoreMetadata::kInvalidId); 1718} 1719 1720void IndexedDBDatabase::VersionChangeAbortOperation( 1721 const string16& previous_version, 1722 int64 previous_int_version, 1723 IndexedDBTransaction* transaction) { 1724 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation"); 1725 DCHECK(!transaction); 1726 metadata_.version = previous_version; 1727 metadata_.int_version = previous_int_version; 1728} 1729 1730} // namespace content 1731