Chromium Code Reviews| Index: content/browser/indexed_db/indexed_db_backing_store.cc |
| diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc |
| index 64e15457774ea87de1cf32ff808660ff7c85a7ea..44a8876c518c1dd18ce614300c3b658e421fe1ca 100644 |
| --- a/content/browser/indexed_db/indexed_db_backing_store.cc |
| +++ b/content/browser/indexed_db/indexed_db_backing_store.cc |
| @@ -128,6 +128,8 @@ enum IndexedDBBackingStoreErrorSource { |
| CLEAR_OBJECT_STORE, |
| READ_BLOB_JOURNAL, |
| DECODE_BLOB_JOURNAL, |
| + GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER, |
| + GET_BLOB_INFO_FOR_RECORD, |
| INTERNAL_ERROR_MAX, |
| }; |
| @@ -304,7 +306,8 @@ const char* IndexedDBBackingStore::Comparator::Name() const { |
| // 0 - Initial version. |
| // 1 - Adds UserIntVersion to DatabaseMetaData. |
| // 2 - Adds DataVersion to to global metadata. |
| -static const int64 kLatestKnownSchemaVersion = 2; |
| +// 3 - Adds metadata needed for blob support. |
| +static const int64 kLatestKnownSchemaVersion = 3; |
| WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) { |
| int64 db_schema_version = 0; |
| bool found = false; |
| @@ -341,15 +344,16 @@ WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) { |
| return true; |
| } |
| -WARN_UNUSED_RESULT static bool SetUpMetadata( |
| - LevelDBDatabase* db, |
| - const std::string& origin_identifier) { |
| +// TODO(ericu): Move this down into the member section of this file. I'm |
| +// leaving it here for this CL as it's easier to see the diffs in place. |
| +WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() { |
| const uint32 latest_known_data_version = |
| blink::kSerializedScriptValueVersion; |
| const std::string schema_version_key = SchemaVersionKey::Encode(); |
| const std::string data_version_key = DataVersionKey::Encode(); |
| - scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db); |
| + scoped_refptr<LevelDBTransaction> transaction = |
| + new LevelDBTransaction(db_.get()); |
| int64 db_schema_version = 0; |
| int64 db_data_version = 0; |
| @@ -366,6 +370,12 @@ WARN_UNUSED_RESULT static bool SetUpMetadata( |
| PutInt(transaction.get(), schema_version_key, db_schema_version); |
| db_data_version = latest_known_data_version; |
| PutInt(transaction.get(), data_version_key, db_data_version); |
| + // If a blob directory already exists for this database, blow it away. It's |
| + // leftover from a partially-purged previous generation of data. |
| + if (!base::DeleteFile(blob_path_, true)) { |
| + INTERNAL_WRITE_ERROR(SET_UP_METADATA); |
|
cmumford
2014/05/06 18:33:11
INTERNAL_READ_ERROR_UNTESTED
ericu
2014/05/06 20:07:42
INTERNAL_WRITE_ERROR_UNTESTED? Switched to that.
|
| + return false; |
| + } |
| } else { |
| // Upgrade old backing store. |
| DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion); |
| @@ -373,10 +383,10 @@ WARN_UNUSED_RESULT static bool SetUpMetadata( |
| db_schema_version = 1; |
| PutInt(transaction.get(), schema_version_key, db_schema_version); |
| const std::string start_key = |
| - DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier); |
| + DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_); |
| const std::string stop_key = |
| - DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier); |
| - scoped_ptr<LevelDBIterator> it = db->CreateIterator(); |
| + DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_); |
| + scoped_ptr<LevelDBIterator> it = db_->CreateIterator(); |
| for (s = it->Seek(start_key); |
| s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0; |
| s = it->Next()) { |
| @@ -404,6 +414,13 @@ WARN_UNUSED_RESULT static bool SetUpMetadata( |
| db_data_version = blink::kSerializedScriptValueVersion; |
| PutInt(transaction.get(), data_version_key, db_data_version); |
| } |
| + if (db_schema_version < 3) { |
| + db_schema_version = 3; |
| + if (!base::DeleteFile(blob_path_, true)) { |
| + INTERNAL_WRITE_ERROR(SET_UP_METADATA); |
|
cmumford
2014/05/06 18:33:11
INTERNAL_READ_ERROR_UNTESTED
ericu
2014/05/06 20:07:42
Same?
cmumford
2014/05/06 20:41:47
Woops - my note was bad (READ vs. WRITE). But yes,
|
| + return false; |
| + } |
| + } |
| } |
| if (!s.ok()) { |
| @@ -480,6 +497,55 @@ class DefaultLevelDBFactory : public LevelDBFactory { |
| } |
| }; |
| +static bool GetBlobKeyGeneratorCurrentNumber( |
| + LevelDBTransaction* leveldb_transaction, |
| + int64 database_id, |
| + int64* blob_key_generator_current_number) { |
| + const std::string key_gen_key = DatabaseMetaDataKey::Encode( |
| + database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER); |
| + |
| + // Default to initial number if not found. |
| + int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber; |
| + std::string data; |
| + |
| + bool found = false; |
| + bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok(); |
| + if (!ok) { |
| + INTERNAL_READ_ERROR(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER); |
|
cmumford
2014/05/06 18:33:11
INTERNAL_READ_ERROR_UNTESTED
ericu
2014/05/06 20:07:42
Done.
|
| + return false; |
| + } |
| + if (found) { |
| + StringPiece slice(data); |
| + if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() || |
| + !DatabaseMetaDataKey::IsValidBlobKey(cur_number)) { |
| + INTERNAL_READ_ERROR(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER); |
|
cmumford
2014/05/06 18:33:11
INTERNAL_READ_ERROR_UNTESTED
ericu
2014/05/06 20:07:42
Done.
|
| + return false; |
| + } |
| + } |
| + *blob_key_generator_current_number = cur_number; |
| + return true; |
| +} |
| + |
| +static bool UpdateBlobKeyGeneratorCurrentNumber( |
| + LevelDBTransaction* leveldb_transaction, |
| + int64 database_id, |
| + int64 blob_key_generator_current_number) { |
| +#ifndef NDEBUG |
| + int64 old_number; |
| + if (!GetBlobKeyGeneratorCurrentNumber( |
| + leveldb_transaction, database_id, &old_number)) |
| + return false; |
| + DCHECK_LT(old_number, blob_key_generator_current_number); |
| +#endif |
| + DCHECK( |
| + DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number)); |
| + const std::string key = DatabaseMetaDataKey::Encode( |
| + database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER); |
| + |
| + PutVarInt(leveldb_transaction, key, blob_key_generator_current_number); |
| + return true; |
| +} |
| + |
| // TODO(ericu): Error recovery. If we persistently can't read the |
| // blob journal, the safe thing to do is to clear it and leak the blobs, |
| // though that may be costly. Still, database/directory deletion should always |
| @@ -573,6 +639,64 @@ static leveldb::Status MergeDatabaseIntoLiveBlobJournal( |
| return leveldb::Status::OK(); |
| } |
| +// Blob Data is encoded as a series of: |
| +// { is_file [bool], key [int64 as varInt], |
| +// type [string-with-length, may be empty], |
| +// (for Blobs only) size [int64 as varInt] |
| +// (for Files only) fileName [string-with-length] |
| +// } |
| +// There is no length field; just read until you run out of data. |
| +static std::string EncodeBlobData( |
| + const std::vector<IndexedDBBlobInfo*>& blob_info) { |
| + std::string ret; |
| + std::vector<IndexedDBBlobInfo*>::const_iterator iter; |
| + for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) { |
| + const IndexedDBBlobInfo& info = **iter; |
| + EncodeBool(info.is_file(), &ret); |
| + EncodeVarInt(info.key(), &ret); |
| + EncodeStringWithLength(info.type(), &ret); |
| + if (info.is_file()) |
| + EncodeStringWithLength(info.file_name(), &ret); |
| + else |
| + EncodeVarInt(info.size(), &ret); |
|
jsbell
2014/05/06 18:29:48
Can you add '(var int') to the docs at the top of
ericu
2014/05/06 20:07:42
Done. That's the blob journal, not this blob data
|
| + } |
| + return ret; |
| +} |
| + |
| +static bool DecodeBlobData(const std::string& data, |
| + std::vector<IndexedDBBlobInfo>* output) { |
| + std::vector<IndexedDBBlobInfo> ret; |
| + output->clear(); |
| + StringPiece slice(data); |
| + while (!slice.empty()) { |
| + bool is_file; |
| + int64 key; |
| + base::string16 type; |
| + int64 size; |
| + base::string16 file_name; |
| + |
| + if (!DecodeBool(&slice, &is_file)) |
| + return false; |
| + if (!DecodeVarInt(&slice, &key) || |
| + !DatabaseMetaDataKey::IsValidBlobKey(key)) |
| + return false; |
| + if (!DecodeStringWithLength(&slice, &type)) |
| + return false; |
| + if (is_file) { |
| + if (!DecodeStringWithLength(&slice, &file_name)) |
| + return false; |
| + ret.push_back(IndexedDBBlobInfo(key, type, file_name)); |
| + } else { |
| + if (!DecodeVarInt(&slice, &size) || size < 0) |
| + return false; |
| + ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key)); |
| + } |
| + } |
| + output->swap(ret); |
| + |
| + return true; |
| +} |
| + |
| IndexedDBBackingStore::IndexedDBBackingStore( |
| IndexedDBFactory* indexed_db_factory, |
| const GURL& origin_url, |
| @@ -589,7 +713,8 @@ IndexedDBBackingStore::IndexedDBBackingStore( |
| task_runner_(task_runner), |
| db_(db.Pass()), |
| comparator_(comparator.Pass()), |
| - active_blob_registry_(this) {} |
| + active_blob_registry_(this) { |
| +} |
| IndexedDBBackingStore::~IndexedDBBackingStore() { |
| if (!blob_path_.empty() && !child_process_ids_granted_.empty()) { |
| @@ -923,13 +1048,19 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( |
| return scoped_refptr<IndexedDBBackingStore>(); |
| } |
| - return Create(indexed_db_factory, |
| - origin_url, |
| - blob_path, |
| - request_context, |
| - db.Pass(), |
| - comparator.Pass(), |
| - task_runner); |
| + scoped_refptr<IndexedDBBackingStore> backing_store = |
| + Create(indexed_db_factory, |
| + origin_url, |
| + blob_path, |
| + request_context, |
| + db.Pass(), |
| + comparator.Pass(), |
| + task_runner); |
| + |
| + if (clean_journal && |
| + !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) |
|
cmumford
2014/05/06 18:33:11
If SetupMetaData fails for whatever reason backing
ericu
2014/05/06 20:07:42
Fixed.
|
| + return scoped_refptr<IndexedDBBackingStore>(); |
|
cmumford
2014/05/06 18:33:11
Do you want to record a histogram value here in ca
ericu
2014/05/06 20:07:42
Yeah, good idea.
|
| + return backing_store; |
| } |
| // static |
| @@ -986,8 +1117,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create( |
| db.Pass(), |
| comparator.Pass(), |
| task_runner)); |
| - if (!SetUpMetadata(backing_store->db_.get(), |
| - backing_store->origin_identifier_)) |
| + if (!backing_store->SetUpMetadata()) |
| return scoped_refptr<IndexedDBBackingStore>(); |
| return backing_store; |
| @@ -1084,6 +1214,29 @@ leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData( |
| INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA); |
| } |
| + // We don't cache this, we just check it if it's there. |
| + int64 blob_key_generator_current_number = |
| + DatabaseMetaDataKey::kInvalidBlobKey; |
| + |
| + s = GetVarInt( |
| + db_.get(), |
| + DatabaseMetaDataKey::Encode( |
| + metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER), |
| + &blob_key_generator_current_number, |
| + found); |
| + if (!s.ok()) { |
| + INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA); |
|
cmumford
2014/05/06 18:33:11
_UNTESTED
ericu
2014/05/06 20:07:42
Done.
|
| + return s; |
| + } |
| + if (!*found) { |
| + // This database predates blob support. |
| + *found = true; |
| + } else if (!DatabaseMetaDataKey::IsValidBlobKey( |
| + blob_key_generator_current_number)) { |
| + INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA); |
|
cmumford
2014/05/06 18:33:11
_UNTESTED
ericu
2014/05/06 20:07:42
Done.
|
| + return InternalInconsistencyStatus(); |
| + } |
| + |
| return s; |
| } |
| @@ -1137,6 +1290,12 @@ leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData( |
| DatabaseMetaDataKey::Encode(*row_id, |
| DatabaseMetaDataKey::USER_INT_VERSION), |
| int_version); |
| + PutVarInt( |
| + transaction.get(), |
| + DatabaseMetaDataKey::Encode( |
| + *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER), |
| + DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber); |
| + |
| s = transaction->Commit(); |
| if (!s.ok()) |
| INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA); |
| @@ -1169,15 +1328,47 @@ static leveldb::Status DeleteRange(LevelDBTransaction* transaction, |
| return s; |
| } |
| +static leveldb::Status DeleteBlobsInObjectStore( |
| + IndexedDBBackingStore::Transaction* transaction, |
| + int64 database_id, |
| + int64 object_store_id) { |
| + std::string start_key, end_key; |
| + start_key = |
| + BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id); |
| + end_key = |
| + BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id); |
| + |
| + scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator(); |
| + for (it->Seek(start_key); |
|
cmumford
2014/05/06 18:33:11
Seek/Next/etc. now return Status objects. should c
ericu
2014/05/06 20:07:42
Working on that now. On the last step of an itera
cmumford
2014/05/06 20:41:47
I believe that it will return OK() and the iterato
|
| + it->IsValid() && CompareKeys(it->Key(), end_key) < 0; |
| + it->Next()) { |
| + StringPiece key_piece(it->Key()); |
| + std::string user_key = |
| + BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece); |
| + if (!user_key.size()) { |
| + INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA); |
|
cmumford
2014/05/06 18:33:11
_UNTESTED
ericu
2014/05/06 20:07:42
Done.
|
| + return InternalInconsistencyStatus(); |
| + } |
| + transaction->PutBlobInfo( |
| + database_id, object_store_id, user_key, NULL, NULL); |
| + } |
| + return leveldb::Status::OK(); |
| +} |
| + |
| leveldb::Status IndexedDBBackingStore::DeleteDatabase( |
| const base::string16& name) { |
| IDB_TRACE("IndexedDBBackingStore::DeleteDatabase"); |
| scoped_ptr<LevelDBDirectTransaction> transaction = |
| LevelDBDirectTransaction::Create(db_.get()); |
| + leveldb::Status s; |
| + s = CleanUpBlobJournal(BlobJournalKey::Encode()); |
| + if (!s.ok()) |
| + return InternalInconsistencyStatus(); |
|
cmumford
2014/05/06 18:33:11
Why not return "s" here?
ericu
2014/05/06 20:07:42
Done.
|
| + |
| IndexedDBDatabaseMetadata metadata; |
| bool success = false; |
| - leveldb::Status s = GetIDBDatabaseMetaData(name, &metadata, &success); |
| + s = GetIDBDatabaseMetaData(name, &metadata, &success); |
| if (!s.ok()) |
| return s; |
| if (!success) |
| @@ -1200,16 +1391,28 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase( |
| const std::string key = DatabaseNameKey::Encode(origin_identifier_, name); |
| transaction->Remove(key); |
| - // TODO(ericu): Put the real calls to the blob journal code here. For now, |
| - // I've inserted fake calls so that we don't get "you didn't use this static |
| - // function" compiler errors. |
| + bool need_cleanup = false; |
| + if (active_blob_registry()->MarkDeletedCheckIfUsed( |
| + metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) { |
| + s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id); |
| + if (!s.ok()) |
| + return s; |
| + } else { |
| + UpdateBlobJournalWithDatabase(transaction.get(), metadata.id); |
| + need_cleanup = true; |
| + } |
| + |
| + // TODO(ericu): Remove these fake calls, added to avoid "defined but unused" |
| + // compiler errors until the code that makes the real calls can be added. |
| if (false) { |
| + std::vector<IndexedDBBlobInfo*> fake; |
| + EncodeBlobData(fake); |
| + |
| scoped_refptr<LevelDBTransaction> fake_transaction = |
| new LevelDBTransaction(NULL); |
| BlobJournalType fake_journal; |
| - MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id); |
| - UpdateBlobJournalWithDatabase(transaction.get(), metadata.id); |
| MergeBlobsIntoLiveBlobJournal(fake_transaction.get(), fake_journal); |
| + UpdateBlobKeyGeneratorCurrentNumber(fake_transaction.get(), 0, 0); |
| } |
| s = transaction->Commit(); |
| @@ -1217,6 +1420,10 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase( |
| INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE); |
| return s; |
| } |
| + |
| + if (need_cleanup) |
| + CleanUpBlobJournal(BlobJournalKey::Encode()); |
| + |
| db_->Compact(start_key, stop_key); |
| return s; |
| } |
| @@ -1525,6 +1732,12 @@ leveldb::Status IndexedDBBackingStore::DeleteObjectStore( |
| return InternalInconsistencyStatus(); |
| } |
| + s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id); |
| + if (!s.ok()) { |
| + INTERNAL_CONSISTENCY_ERROR(DELETE_OBJECT_STORE); |
|
cmumford
2014/05/06 18:33:11
_UNTESTED
ericu
2014/05/06 20:07:42
Done.
|
| + return s; |
| + } |
| + |
| s = DeleteRange( |
| leveldb_transaction, |
| ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0), |
| @@ -1593,7 +1806,7 @@ leveldb::Status IndexedDBBackingStore::GetRecord( |
| } |
| record->bits = slice.as_string(); |
| - return s; |
| + return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record); |
| } |
| WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber( |
| @@ -1689,9 +1902,11 @@ leveldb::Status IndexedDBBackingStore::ClearObjectStore( |
| leveldb::Status s = |
| DeleteRange(transaction->transaction(), start_key, stop_key); |
| - if (!s.ok()) |
| + if (!s.ok()) { |
| INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE); |
| - return s; |
| + return s; |
| + } |
| + return DeleteBlobsInObjectStore(transaction, database_id, object_store_id); |
| } |
| leveldb::Status IndexedDBBackingStore::DeleteRecord( |
| @@ -2289,6 +2504,68 @@ leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal( |
| return journal_transaction->Commit(); |
| } |
| +leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord( |
| + int64 database_id, |
| + const std::string& object_store_data_key, |
| + IndexedDBValue* value) { |
| + BlobChangeRecord* change_record = NULL; |
| + BlobChangeMap::const_iterator blob_iter = |
| + blob_change_map_.find(object_store_data_key); |
| + if (blob_iter != blob_change_map_.end()) |
| + change_record = blob_iter->second; |
| + else { |
|
jsbell
2014/05/06 18:29:48
Inconsistent bracing (if any if/else branch uses b
ericu
2014/05/06 20:07:42
Done.
|
| + blob_iter = incognito_blob_map_.find(object_store_data_key); |
| + if (blob_iter != incognito_blob_map_.end()) |
| + change_record = blob_iter->second; |
| + } |
| + if (change_record) { |
| + // Either we haven't written the blob to disk yet or we're in incognito |
| + // mode, so we have to send back the one they sent us. This change record |
| + // includes the original UUID. |
| + value->blob_info = change_record->blob_info(); |
| + return leveldb::Status::OK(); |
| + } |
| + |
| + BlobEntryKey blob_entry_key; |
| + StringPiece leveldb_key_piece(object_store_data_key); |
| + if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece, |
| + &blob_entry_key)) { |
| + NOTREACHED(); |
| + return InternalInconsistencyStatus(); |
| + } |
| + scoped_ptr<LevelDBIterator> it = transaction()->CreateIterator(); |
| + std::string encoded_key = blob_entry_key.Encode(); |
| + it->Seek(encoded_key); |
|
cmumford
2014/05/06 18:33:11
Status
ericu
2014/05/06 20:07:42
Done.
|
| + if (it->IsValid() && CompareKeys(it->Key(), encoded_key) == 0) { |
| + if (!DecodeBlobData(it->Value().as_string(), &value->blob_info)) { |
| + INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD); |
| + return InternalInconsistencyStatus(); |
| + } |
| + std::vector<IndexedDBBlobInfo>::iterator iter; |
| + for (iter = value->blob_info.begin(); iter != value->blob_info.end(); |
| + ++iter) { |
| + iter->set_file_path( |
| + backing_store_->GetBlobFileName(database_id, iter->key())); |
| + iter->set_mark_used_callback( |
| + backing_store_->active_blob_registry()->GetAddBlobRefCallback( |
| + database_id, iter->key())); |
| + iter->set_release_callback( |
| + backing_store_->active_blob_registry()->GetFinalReleaseCallback( |
| + database_id, iter->key())); |
| + if (iter->is_file()) { |
| + base::File::Info info; |
| + if (base::GetFileInfo(iter->file_path(), &info)) { |
| + // This should always work, but it isn't fatal if it doesn't; it just |
| + // means a potential slow synchronous call from the renderer later. |
| + iter->set_last_modified(info.last_modified); |
| + iter->set_size(info.size); |
| + } |
| + } |
| + } |
| + } |
| + return leveldb::Status::OK(); |
| +} |
| + |
| void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() { |
| CleanUpBlobJournal(BlobJournalKey::Encode()); |
| } |
| @@ -2612,11 +2889,13 @@ leveldb::Status IndexedDBBackingStore::KeyExistsInIndex( |
| IndexedDBBackingStore::Cursor::Cursor( |
| const IndexedDBBackingStore::Cursor* other) |
| - : transaction_(other->transaction_), |
| + : backing_store_(other->backing_store_), |
| + transaction_(other->transaction_), |
| + database_id_(other->database_id_), |
| cursor_options_(other->cursor_options_), |
| current_key_(new IndexedDBKey(*other->current_key_)) { |
| if (other->iterator_) { |
| - iterator_ = transaction_->CreateIterator(); |
| + iterator_ = transaction_->transaction()->CreateIterator(); |
| if (other->iterator_->IsValid()) { |
| leveldb::Status s = iterator_->Seek(other->iterator_->Key()); |
| @@ -2626,13 +2905,20 @@ IndexedDBBackingStore::Cursor::Cursor( |
| } |
| } |
| -IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction* transaction, |
| - const CursorOptions& cursor_options) |
| - : transaction_(transaction), cursor_options_(cursor_options) {} |
| +IndexedDBBackingStore::Cursor::Cursor( |
| + scoped_refptr<IndexedDBBackingStore> backing_store, |
| + IndexedDBBackingStore::Transaction* transaction, |
| + int64 database_id, |
| + const CursorOptions& cursor_options) |
| + : backing_store_(backing_store), |
| + transaction_(transaction), |
| + database_id_(database_id), |
| + cursor_options_(cursor_options) { |
| +} |
| IndexedDBBackingStore::Cursor::~Cursor() {} |
| bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) { |
| - iterator_ = transaction_->CreateIterator(); |
| + iterator_ = transaction_->transaction()->CreateIterator(); |
| if (cursor_options_.forward) |
| *s = iterator_->Seek(cursor_options_.low_key); |
| else |
| @@ -2815,9 +3101,14 @@ IndexedDBBackingStore::Cursor::record_identifier() const { |
| class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor { |
| public: |
| ObjectStoreKeyCursorImpl( |
| - LevelDBTransaction* transaction, |
| + scoped_refptr<IndexedDBBackingStore> backing_store, |
| + IndexedDBBackingStore::Transaction* transaction, |
| + int64 database_id, |
| const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) |
| - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} |
| + : IndexedDBBackingStore::Cursor(backing_store, |
| + transaction, |
| + database_id, |
| + cursor_options) {} |
| virtual Cursor* Clone() OVERRIDE { |
| return new ObjectStoreKeyCursorImpl(this); |
| @@ -2874,9 +3165,14 @@ bool ObjectStoreKeyCursorImpl::LoadCurrentRow() { |
| class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor { |
| public: |
| ObjectStoreCursorImpl( |
| - LevelDBTransaction* transaction, |
| + scoped_refptr<IndexedDBBackingStore> backing_store, |
| + IndexedDBBackingStore::Transaction* transaction, |
| + int64 database_id, |
| const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) |
| - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} |
| + : IndexedDBBackingStore::Cursor(backing_store, |
| + transaction, |
| + database_id, |
| + cursor_options) {} |
| virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); } |
| @@ -2925,6 +3221,11 @@ bool ObjectStoreCursorImpl::LoadCurrentRow() { |
| EncodeIDBKey(*current_key_, &encoded_key); |
| record_identifier_.Reset(encoded_key, version); |
| + if (!transaction_->GetBlobInfoForRecord(database_id_, |
| + iterator_->Key().as_string(), |
| + ¤t_value_).ok()) { |
| + return false; |
| + } |
| current_value_.bits = value_slice.as_string(); |
| return true; |
| } |
| @@ -2932,9 +3233,14 @@ bool ObjectStoreCursorImpl::LoadCurrentRow() { |
| class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor { |
| public: |
| IndexKeyCursorImpl( |
| - LevelDBTransaction* transaction, |
| + scoped_refptr<IndexedDBBackingStore> backing_store, |
| + IndexedDBBackingStore::Transaction* transaction, |
| + int64 database_id, |
| const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) |
| - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} |
| + : IndexedDBBackingStore::Cursor(backing_store, |
| + transaction, |
| + database_id, |
| + cursor_options) {} |
| virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); } |
| @@ -3007,13 +3313,14 @@ bool IndexKeyCursorImpl::LoadCurrentRow() { |
| std::string result; |
| bool found = false; |
| - leveldb::Status s = transaction_->Get(primary_leveldb_key, &result, &found); |
| + leveldb::Status s = |
| + transaction_->transaction()->Get(primary_leveldb_key, &result, &found); |
| if (!s.ok()) { |
| INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW); |
| return false; |
| } |
| if (!found) { |
| - transaction_->Remove(iterator_->Key()); |
| + transaction_->transaction()->Remove(iterator_->Key()); |
| return false; |
| } |
| if (!result.size()) { |
| @@ -3029,7 +3336,7 @@ bool IndexKeyCursorImpl::LoadCurrentRow() { |
| } |
| if (object_store_data_version != index_data_version) { |
| - transaction_->Remove(iterator_->Key()); |
| + transaction_->transaction()->Remove(iterator_->Key()); |
| return false; |
| } |
| @@ -3039,9 +3346,14 @@ bool IndexKeyCursorImpl::LoadCurrentRow() { |
| class IndexCursorImpl : public IndexedDBBackingStore::Cursor { |
| public: |
| IndexCursorImpl( |
| - LevelDBTransaction* transaction, |
| + scoped_refptr<IndexedDBBackingStore> backing_store, |
| + IndexedDBBackingStore::Transaction* transaction, |
| + int64 database_id, |
| const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) |
| - : IndexedDBBackingStore::Cursor(transaction, cursor_options) {} |
| + : IndexedDBBackingStore::Cursor(backing_store, |
| + transaction, |
| + database_id, |
| + cursor_options) {} |
| virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); } |
| @@ -3107,6 +3419,7 @@ bool IndexCursorImpl::LoadCurrentRow() { |
| return false; |
| } |
| + DCHECK_EQ(index_data_key.DatabaseId(), database_id_); |
| primary_leveldb_key_ = |
| ObjectStoreDataKey::Encode(index_data_key.DatabaseId(), |
| index_data_key.ObjectStoreId(), |
| @@ -3114,13 +3427,14 @@ bool IndexCursorImpl::LoadCurrentRow() { |
| std::string result; |
| bool found = false; |
| - leveldb::Status s = transaction_->Get(primary_leveldb_key_, &result, &found); |
| + leveldb::Status s = |
| + transaction_->transaction()->Get(primary_leveldb_key_, &result, &found); |
| if (!s.ok()) { |
| INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW); |
| return false; |
| } |
| if (!found) { |
| - transaction_->Remove(iterator_->Key()); |
| + transaction_->transaction()->Remove(iterator_->Key()); |
| return false; |
| } |
| if (!result.size()) { |
| @@ -3136,12 +3450,14 @@ bool IndexCursorImpl::LoadCurrentRow() { |
| } |
| if (object_store_data_version != index_data_version) { |
| - transaction_->Remove(iterator_->Key()); |
| + transaction_->transaction()->Remove(iterator_->Key()); |
| return false; |
| } |
| current_value_.bits = slice.as_string(); |
| - return true; |
| + return transaction_->GetBlobInfoForRecord(database_id_, |
| + primary_leveldb_key_, |
| + ¤t_value_).ok(); |
| } |
| bool ObjectStoreCursorOptions( |
| @@ -3308,8 +3624,8 @@ IndexedDBBackingStore::OpenObjectStoreCursor( |
| direction, |
| &cursor_options)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |
| - scoped_ptr<ObjectStoreCursorImpl> cursor( |
| - new ObjectStoreCursorImpl(leveldb_transaction, cursor_options)); |
| + scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl( |
| + this, transaction, database_id, cursor_options)); |
| if (!cursor->FirstSeek(s)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |
| @@ -3335,8 +3651,8 @@ IndexedDBBackingStore::OpenObjectStoreKeyCursor( |
| direction, |
| &cursor_options)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |
| - scoped_ptr<ObjectStoreKeyCursorImpl> cursor( |
| - new ObjectStoreKeyCursorImpl(leveldb_transaction, cursor_options)); |
| + scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl( |
| + this, transaction, database_id, cursor_options)); |
| if (!cursor->FirstSeek(s)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |
| @@ -3365,7 +3681,7 @@ IndexedDBBackingStore::OpenIndexKeyCursor( |
| &cursor_options)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |
| scoped_ptr<IndexKeyCursorImpl> cursor( |
| - new IndexKeyCursorImpl(leveldb_transaction, cursor_options)); |
| + new IndexKeyCursorImpl(this, transaction, database_id, cursor_options)); |
| if (!cursor->FirstSeek(s)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |
| @@ -3393,7 +3709,7 @@ IndexedDBBackingStore::OpenIndexCursor( |
| &cursor_options)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |
| scoped_ptr<IndexCursorImpl> cursor( |
| - new IndexCursorImpl(leveldb_transaction, cursor_options)); |
| + new IndexCursorImpl(this, transaction, database_id, cursor_options)); |
| if (!cursor->FirstSeek(s)) |
| return scoped_ptr<IndexedDBBackingStore::Cursor>(); |