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