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