indexed_db_database.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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 DCHECK(transactions_.find(transaction_id) == transactions_.end()); 1402 if (transactions_.find(transaction_id) != transactions_.end()) 1403 return; 1404 1405 scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction( 1406 transaction_id, 1407 connection->callbacks(), 1408 std::set<int64>(object_store_ids.begin(), object_store_ids.end()), 1409 static_cast<indexed_db::TransactionMode>(mode), 1410 this); 1411 transactions_[transaction_id] = transaction; 1412} 1413 1414bool IndexedDBDatabase::IsOpenConnectionBlocked() const { 1415 return !pending_delete_calls_.empty() || 1416 running_version_change_transaction_ || 1417 pending_run_version_change_transaction_call_; 1418} 1419 1420void IndexedDBDatabase::OpenConnection( 1421 scoped_refptr<IndexedDBCallbacks> callbacks, 1422 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks, 1423 int64 transaction_id, 1424 int64 version) { 1425 const WebKit::WebIDBCallbacks::DataLoss kDataLoss = 1426 WebKit::WebIDBCallbacks::DataLossNone; 1427 OpenConnection( 1428 callbacks, database_callbacks, transaction_id, version, kDataLoss); 1429} 1430 1431void IndexedDBDatabase::OpenConnection( 1432 scoped_refptr<IndexedDBCallbacks> callbacks, 1433 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks, 1434 int64 transaction_id, 1435 int64 version, 1436 WebKit::WebIDBCallbacks::DataLoss data_loss) { 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( 1505 callbacks, connection.Pass(), transaction_id, version, data_loss); 1506 return; 1507 } 1508 if (version < metadata_.int_version) { 1509 callbacks->OnError(IndexedDBDatabaseError( 1510 WebKit::WebIDBDatabaseExceptionVersionError, 1511 ASCIIToUTF16("The requested version (") + Int64ToString16(version) + 1512 ASCIIToUTF16(") is less than the existing version (") + 1513 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(")."))); 1514 return; 1515 } 1516 DCHECK_EQ(version, metadata_.int_version); 1517 connections_.insert(connection.get()); 1518 callbacks->OnSuccess(connection.Pass(), this->metadata()); 1519} 1520 1521void IndexedDBDatabase::RunVersionChangeTransaction( 1522 scoped_refptr<IndexedDBCallbacks> callbacks, 1523 scoped_ptr<IndexedDBConnection> connection, 1524 int64 transaction_id, 1525 int64 requested_version, 1526 WebKit::WebIDBCallbacks::DataLoss data_loss) { 1527 1528 DCHECK(callbacks); 1529 DCHECK(connections_.has(connection.get())); 1530 if (ConnectionCount() > 1) { 1531 DCHECK_NE(WebKit::WebIDBCallbacks::DataLossTotal, data_loss); 1532 // Front end ensures the event is not fired at connections that have 1533 // close_pending set. 1534 for (ConnectionSet::const_iterator it = connections_.begin(); 1535 it != connections_.end(); 1536 ++it) { 1537 if (*it != connection.get()) { 1538 (*it)->callbacks()->OnVersionChange(metadata_.int_version, 1539 requested_version); 1540 } 1541 } 1542 // TODO(jsbell): Remove the call to OnBlocked and instead wait 1543 // until the frontend tells us that all the "versionchange" events 1544 // have been delivered. http://crbug.com/100123 1545 callbacks->OnBlocked(metadata_.int_version); 1546 1547 DCHECK(!pending_run_version_change_transaction_call_); 1548 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall( 1549 callbacks, connection.Pass(), transaction_id, requested_version)); 1550 return; 1551 } 1552 RunVersionChangeTransactionFinal(callbacks, 1553 connection.Pass(), 1554 transaction_id, 1555 requested_version, 1556 data_loss); 1557} 1558 1559void IndexedDBDatabase::RunVersionChangeTransactionFinal( 1560 scoped_refptr<IndexedDBCallbacks> callbacks, 1561 scoped_ptr<IndexedDBConnection> connection, 1562 int64 transaction_id, 1563 int64 requested_version) { 1564 const WebKit::WebIDBCallbacks::DataLoss kDataLoss = 1565 WebKit::WebIDBCallbacks::DataLossNone; 1566 RunVersionChangeTransactionFinal(callbacks, 1567 connection.Pass(), 1568 transaction_id, 1569 requested_version, 1570 kDataLoss); 1571} 1572 1573void IndexedDBDatabase::RunVersionChangeTransactionFinal( 1574 scoped_refptr<IndexedDBCallbacks> callbacks, 1575 scoped_ptr<IndexedDBConnection> connection, 1576 int64 transaction_id, 1577 int64 requested_version, 1578 WebKit::WebIDBCallbacks::DataLoss data_loss) { 1579 1580 std::vector<int64> object_store_ids; 1581 CreateTransaction(transaction_id, 1582 connection.get(), 1583 object_store_ids, 1584 indexed_db::TRANSACTION_VERSION_CHANGE); 1585 scoped_refptr<IndexedDBTransaction> transaction = 1586 transactions_[transaction_id]; 1587 1588 transaction->ScheduleTask( 1589 base::Bind(&IndexedDBDatabase::VersionChangeOperation, 1590 this, 1591 requested_version, 1592 callbacks, 1593 base::Passed(&connection), 1594 data_loss), 1595 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, 1596 this, 1597 metadata_.version, 1598 metadata_.int_version)); 1599 1600 DCHECK(!pending_second_half_open_); 1601} 1602 1603void IndexedDBDatabase::DeleteDatabase( 1604 scoped_refptr<IndexedDBCallbacks> callbacks) { 1605 1606 if (IsDeleteDatabaseBlocked()) { 1607 for (ConnectionSet::const_iterator it = connections_.begin(); 1608 it != connections_.end(); 1609 ++it) { 1610 // Front end ensures the event is not fired at connections that have 1611 // close_pending set. 1612 (*it)->callbacks()->OnVersionChange( 1613 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION); 1614 } 1615 // TODO(jsbell): Only fire OnBlocked if there are open 1616 // connections after the VersionChangeEvents are received, not 1617 // just set up to fire. http://crbug.com/100123 1618 callbacks->OnBlocked(metadata_.int_version); 1619 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks)); 1620 return; 1621 } 1622 DeleteDatabaseFinal(callbacks); 1623} 1624 1625bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const { 1626 return !!ConnectionCount(); 1627} 1628 1629void IndexedDBDatabase::DeleteDatabaseFinal( 1630 scoped_refptr<IndexedDBCallbacks> callbacks) { 1631 DCHECK(!IsDeleteDatabaseBlocked()); 1632 DCHECK(backing_store_); 1633 if (!backing_store_->DeleteDatabase(metadata_.name)) { 1634 callbacks->OnError( 1635 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 1636 "Internal error deleting database.")); 1637 return; 1638 } 1639 metadata_.version = kNoStringVersion; 1640 metadata_.id = kInvalidId; 1641 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION; 1642 metadata_.object_stores.clear(); 1643 callbacks->OnSuccess(); 1644} 1645 1646void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) { 1647 DCHECK(connections_.has(connection)); 1648 1649 // Close outstanding transactions from the closing connection. This 1650 // can not happen if the close is requested by the connection itself 1651 // as the front-end defers the close until all transactions are 1652 // complete, so something unusual has happened e.g. unexpected 1653 // process termination. 1654 { 1655 TransactionMap transactions(transactions_); 1656 for (TransactionMap::const_iterator it = transactions.begin(), 1657 end = transactions.end(); 1658 it != end; 1659 ++it) { 1660 if (it->second->connection() == connection->callbacks()) 1661 it->second->Abort( 1662 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError, 1663 "Connection is closing.")); 1664 } 1665 } 1666 1667 connections_.erase(connection); 1668 if (pending_second_half_open_ && 1669 pending_second_half_open_->Connection() == connection) { 1670 pending_second_half_open_->Callbacks()->OnError( 1671 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError, 1672 "The connection was closed.")); 1673 pending_second_half_open_.reset(); 1674 } 1675 1676 // process_pending_calls allows the inspector to process a pending open call 1677 // and call close, reentering IndexedDBDatabase::close. Then the 1678 // backend would be removed both by the inspector closing its connection, and 1679 // by the connection that first called close. 1680 // To avoid that situation, don't proceed in case of reentrancy. 1681 if (closing_connection_) 1682 return; 1683 base::AutoReset<bool> ClosingConnection(&closing_connection_, true); 1684 ProcessPendingCalls(); 1685 1686 // TODO(jsbell): Add a test for the pending_open_calls_ cases below. 1687 if (!ConnectionCount() && !pending_open_calls_.size() && 1688 !pending_delete_calls_.size()) { 1689 DCHECK(transactions_.empty()); 1690 1691 // factory_ should only be null in unit tests. 1692 // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow. 1693 if (factory_) { 1694 DCHECK(backing_store_.get()); 1695 factory_->ReleaseDatabase(identifier_, forced); 1696 factory_ = NULL; 1697 } 1698 1699 // Drop reference to backing store after informing factory, so 1700 // that factory can do accounting on it. 1701 backing_store_ = NULL; 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