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