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