Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / indexed_db / indexed_db_backing_store.cc
index 0a83a91..93db051 100644 (file)
@@ -24,6 +24,7 @@
 #include "content/browser/indexed_db/indexed_db_value.h"
 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
 #include "content/common/indexed_db/indexed_db_key.h"
@@ -347,7 +348,7 @@ WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
 
 // 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() {
+WARN_UNUSED_RESULT leveldb::Status IndexedDBBackingStore::SetUpMetadata() {
   const uint32 latest_known_data_version =
       blink::kSerializedScriptValueVersion;
   const std::string schema_version_key = SchemaVersionKey::Encode();
@@ -363,7 +364,7 @@ WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
       GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
   if (!s.ok()) {
     INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
-    return false;
+    return s;
   }
   if (!found) {
     // Initialize new backing store.
@@ -375,7 +376,7 @@ WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
     // leftover from a partially-purged previous generation of data.
     if (!base::DeleteFile(blob_path_, true)) {
       INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
-      return false;
+      return IOErrorStatus();
     }
   } else {
     // Upgrade old backing store.
@@ -396,11 +397,11 @@ WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
         s = GetInt(transaction.get(), it->Key(), &database_id, &found);
         if (!s.ok()) {
           INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
-          return false;
+          return s;
         }
         if (!found) {
           INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
-          return false;
+          return InternalInconsistencyStatus();
         }
         std::string int_version_key = DatabaseMetaDataKey::Encode(
             database_id, DatabaseMetaDataKey::USER_INT_VERSION);
@@ -419,14 +420,14 @@ WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
       db_schema_version = 3;
       if (!base::DeleteFile(blob_path_, true)) {
         INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
-        return false;
+        return IOErrorStatus();
       }
     }
   }
 
   if (!s.ok()) {
     INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
-    return false;
+    return s;
   }
 
   // All new values will be written using this serialization version.
@@ -434,11 +435,11 @@ WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
   s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
   if (!s.ok()) {
     INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
-    return false;
+    return s;
   }
   if (!found) {
     INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
-    return false;
+    return InternalInconsistencyStatus();
   }
   if (db_data_version < latest_known_data_version) {
     db_data_version = latest_known_data_version;
@@ -449,11 +450,9 @@ WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
   DCHECK_EQ(db_data_version, latest_known_data_version);
 
   s = transaction->Commit();
-  if (!s.ok()) {
+  if (!s.ok())
     INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
-    return false;
-  }
-  return true;
+  return s;
 }
 
 template <typename DBOrTransaction>
@@ -564,7 +563,7 @@ static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
   bool found = false;
   leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
   if (!s.ok()) {
-    INTERNAL_READ_ERROR_UNTESTED(READ_BLOB_JOURNAL);
+    INTERNAL_READ_ERROR(READ_BLOB_JOURNAL);
     return s;
   }
   journal->clear();
@@ -572,7 +571,7 @@ static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
     return leveldb::Status::OK();
   StringPiece slice(data);
   if (!DecodeBlobJournal(&slice, journal)) {
-    INTERNAL_READ_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
+    INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
     s = InternalInconsistencyStatus();
   }
   return s;
@@ -709,7 +708,7 @@ IndexedDBBackingStore::IndexedDBBackingStore(
     net::URLRequestContext* request_context,
     scoped_ptr<LevelDBDatabase> db,
     scoped_ptr<LevelDBComparator> comparator,
-    base::TaskRunner* task_runner)
+    base::SequencedTaskRunner* task_runner)
     : indexed_db_factory_(indexed_db_factory),
       origin_url_(origin_url),
       blob_path_(blob_path),
@@ -781,8 +780,9 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
     blink::WebIDBDataLoss* data_loss,
     std::string* data_loss_message,
     bool* disk_full,
-    base::TaskRunner* task_runner,
-    bool clean_journal) {
+    base::SequencedTaskRunner* task_runner,
+    bool clean_journal,
+    leveldb::Status* status) {
   *data_loss = blink::WebIDBDataLossNone;
   DefaultLevelDBFactory leveldb_factory;
   return IndexedDBBackingStore::Open(indexed_db_factory,
@@ -794,7 +794,8 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
                                      disk_full,
                                      &leveldb_factory,
                                      task_runner,
-                                     clean_journal);
+                                     clean_journal,
+                                     status);
 }
 
 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
@@ -864,7 +865,7 @@ leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
 
 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
                                                const GURL& origin_url,
-                                               std::string& message) {
+                                               std::string* message) {
   const base::FilePath info_path =
       path_base.Append(ComputeCorruptionFileName(origin_url));
 
@@ -891,7 +892,7 @@ bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
       if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
         base::DictionaryValue* dict_val =
             static_cast<base::DictionaryValue*>(val.get());
-        success = dict_val->GetString("message", &message);
+        success = dict_val->GetString("message", message);
       }
     }
     file.Close();
@@ -934,14 +935,17 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
     std::string* data_loss_message,
     bool* is_disk_full,
     LevelDBFactory* leveldb_factory,
-    base::TaskRunner* task_runner,
-    bool clean_journal) {
+    base::SequencedTaskRunner* task_runner,
+    bool clean_journal,
+    leveldb::Status* status) {
   IDB_TRACE("IndexedDBBackingStore::Open");
   DCHECK(!path_base.empty());
   *data_loss = blink::WebIDBDataLossNone;
   *data_loss_message = "";
   *is_disk_full = false;
 
+  *status = leveldb::Status::OK();
+
   scoped_ptr<LevelDBComparator> comparator(new Comparator());
 
   if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
@@ -949,8 +953,10 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
                         origin_url);
   }
   if (!base::CreateDirectory(path_base)) {
-    LOG(ERROR) << "Unable to create IndexedDB database path "
-               << path_base.AsUTF8Unsafe();
+    *status =
+        leveldb::Status::IOError("Unable to create IndexedDB database path");
+    LOG(ERROR) << status->ToString() << ": \"" << path_base.AsUTF8Unsafe()
+               << "\"";
     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
                         origin_url);
     return scoped_refptr<IndexedDBBackingStore>();
@@ -962,29 +968,30 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
       path_base.Append(ComputeBlobPath(origin_url));
 
   if (IsPathTooLong(file_path)) {
+    *status = leveldb::Status::IOError("File path too long");
     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
                         origin_url);
     return scoped_refptr<IndexedDBBackingStore>();
   }
 
   scoped_ptr<LevelDBDatabase> db;
-  leveldb::Status status = leveldb_factory->OpenLevelDB(
+  *status = leveldb_factory->OpenLevelDB(
       file_path, comparator.get(), &db, is_disk_full);
 
-  DCHECK(!db == !status.ok());
-  if (!status.ok()) {
-    if (leveldb_env::IndicatesDiskFull(status)) {
+  DCHECK(!db == !status->ok());
+  if (!status->ok()) {
+    if (leveldb_env::IndicatesDiskFull(*status)) {
       *is_disk_full = true;
-    } else if (leveldb_env::IsCorruption(status)) {
+    } else if (leveldb_env::IsCorruption(*status)) {
       *data_loss = blink::WebIDBDataLossTotal;
-      *data_loss_message = leveldb_env::GetCorruptionMessage(status);
+      *data_loss_message = leveldb_env::GetCorruptionMessage(*status);
     }
   }
 
   bool is_schema_known = false;
   if (db) {
     std::string corruption_message;
-    if (ReadCorruptionInfo(path_base, origin_url, corruption_message)) {
+    if (ReadCorruptionInfo(path_base, origin_url, &corruption_message)) {
       LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
                     "database.";
       HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
@@ -1013,21 +1020,21 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
     }
   }
 
-  DCHECK(status.ok() || !is_schema_known || leveldb_env::IsIOError(status) ||
-         leveldb_env::IsCorruption(status));
+  DCHECK(status->ok() || !is_schema_known || leveldb_env::IsIOError(*status) ||
+         leveldb_env::IsCorruption(*status));
 
   if (db) {
     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
-  } else if (leveldb_env::IsIOError(status)) {
+  } else if (leveldb_env::IsIOError(*status)) {
     LOG(ERROR) << "Unable to open backing store, not trying to recover - "
-               << status.ToString();
+               << status->ToString();
     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
     return scoped_refptr<IndexedDBBackingStore>();
   } else {
-    DCHECK(!is_schema_known || leveldb_env::IsCorruption(status));
+    DCHECK(!is_schema_known || leveldb_env::IsCorruption(*status));
     LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
-    status = leveldb_factory->DestroyLevelDB(file_path);
-    if (!status.ok()) {
+    *status = leveldb_factory->DestroyLevelDB(file_path);
+    if (!status->ok()) {
       LOG(ERROR) << "IndexedDB backing store cleanup failed";
       HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
                           origin_url);
@@ -1060,7 +1067,8 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
              request_context,
              db.Pass(),
              comparator.Pass(),
-             task_runner);
+             task_runner,
+             status);
 
   if (clean_journal && backing_store &&
       !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
@@ -1074,17 +1082,19 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
 // static
 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
     const GURL& origin_url,
-    base::TaskRunner* task_runner) {
+    base::SequencedTaskRunner* task_runner,
+    leveldb::Status* status) {
   DefaultLevelDBFactory leveldb_factory;
   return IndexedDBBackingStore::OpenInMemory(
-      origin_url, &leveldb_factory, task_runner);
+      origin_url, &leveldb_factory, task_runner, status);
 }
 
 // static
 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
     const GURL& origin_url,
     LevelDBFactory* leveldb_factory,
-    base::TaskRunner* task_runner) {
+    base::SequencedTaskRunner* task_runner,
+    leveldb::Status* status) {
   IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
 
   scoped_ptr<LevelDBComparator> comparator(new Comparator());
@@ -1104,7 +1114,8 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
                 NULL /* request_context */,
                 db.Pass(),
                 comparator.Pass(),
-                task_runner);
+                task_runner,
+                status);
 }
 
 // static
@@ -1115,7 +1126,8 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
     net::URLRequestContext* request_context,
     scoped_ptr<LevelDBDatabase> db,
     scoped_ptr<LevelDBComparator> comparator,
-    base::TaskRunner* task_runner) {
+    base::SequencedTaskRunner* task_runner,
+    leveldb::Status* status) {
   // TODO(jsbell): Handle comparator name changes.
   scoped_refptr<IndexedDBBackingStore> backing_store(
       new IndexedDBBackingStore(indexed_db_factory,
@@ -1125,7 +1137,8 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
                                 db.Pass(),
                                 comparator.Pass(),
                                 task_runner));
-  if (!backing_store->SetUpMetadata())
+  *status = backing_store->SetUpMetadata();
+  if (!status->ok())
     return scoped_refptr<IndexedDBBackingStore>();
 
   return backing_store;
@@ -1154,6 +1167,7 @@ std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
   for (*s = it->Seek(start_key);
        s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
        *s = it->Next()) {
+    // Decode database name (in iterator key).
     StringPiece slice(it->Key());
     DatabaseNameKey database_name_key;
     if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
@@ -1161,11 +1175,35 @@ std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
       continue;
     }
-    found_names.push_back(database_name_key.database_name());
+
+    // Decode database id (in iterator value).
+    int64 database_id = 0;
+    StringPiece valueSlice(it->Value());
+    if (!DecodeVarInt(&valueSlice, &database_id) || !valueSlice.empty()) {
+      INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
+      continue;
+    }
+
+    // Look up version by id.
+    bool found = false;
+    int64 database_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
+    *s = GetVarInt(db_.get(),
+                   DatabaseMetaDataKey::Encode(
+                       database_id, DatabaseMetaDataKey::USER_INT_VERSION),
+                   &database_version,
+                   &found);
+    if (!s->ok() || !found) {
+      INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
+      continue;
+    }
+
+    // Ignore stale metadata from failed initial opens.
+    if (database_version != IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
+      found_names.push_back(database_name_key.database_name());
   }
 
   if (!s->ok())
-    INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
+    INTERNAL_READ_ERROR(GET_DATABASE_NAMES);
 
   return found_names;
 }
@@ -1276,6 +1314,7 @@ leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
     const base::string16& version,
     int64 int_version,
     int64* row_id) {
+  // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
   scoped_refptr<LevelDBTransaction> transaction =
       IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
 
@@ -1861,7 +1900,7 @@ leveldb::Status IndexedDBBackingStore::PutRecord(
     int64 database_id,
     int64 object_store_id,
     const IndexedDBKey& key,
-    IndexedDBValue& value,
+    IndexedDBValue* value,
     ScopedVector<webkit_blob::BlobDataHandle>* handles,
     RecordIdentifier* record_identifier) {
   IDB_TRACE("IndexedDBBackingStore::PutRecord");
@@ -1881,13 +1920,13 @@ leveldb::Status IndexedDBBackingStore::PutRecord(
 
   std::string v;
   EncodeVarInt(version, &v);
-  v.append(value.bits);
+  v.append(value->bits);
 
   leveldb_transaction->Put(object_store_data_key, &v);
   s = transaction->PutBlobInfoIfNeeded(database_id,
                                        object_store_id,
                                        object_store_data_key,
-                                       &value.blob_info,
+                                       &value->blob_info,
                                        handles);
   if (!s.ok())
     return s;
@@ -1961,7 +2000,7 @@ leveldb::Status IndexedDBBackingStore::DeleteRange(
                             database_id,
                             object_store_id,
                             key_range,
-                            indexed_db::CURSOR_NEXT,
+                            blink::WebIDBCursorDirectionNext,
                             &s);
   if (!s.ok())
     return s;
@@ -1973,7 +2012,7 @@ leveldb::Status IndexedDBBackingStore::DeleteRange(
                             database_id,
                             object_store_id,
                             key_range,
-                            indexed_db::CURSOR_PREV,
+                            blink::WebIDBCursorDirectionPrev,
                             &s);
 
   if (!s.ok())
@@ -2161,14 +2200,14 @@ class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
   ChainedBlobWriterImpl(
       int64 database_id,
       IndexedDBBackingStore* backing_store,
-      WriteDescriptorVec& blobs,
+      WriteDescriptorVec* blobs,
       scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
       : waiting_for_callback_(false),
         database_id_(database_id),
         backing_store_(backing_store),
         callback_(callback),
         aborted_(false) {
-    blobs_.swap(blobs);
+    blobs_.swap(*blobs);
     iter_ = blobs_.begin();
     backing_store->task_runner()->PostTask(
         FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
@@ -2240,11 +2279,11 @@ class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
 };
 
 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
-                          public base::RefCounted<LocalWriteClosure> {
+                          public base::RefCountedThreadSafe<LocalWriteClosure> {
  public:
   LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
                         chained_blob_writer,
-                    base::TaskRunner* task_runner)
+                    base::SequencedTaskRunner* task_runner)
       : chained_blob_writer_(chained_blob_writer),
         task_runner_(task_runner),
         bytes_written_(0) {}
@@ -2264,9 +2303,11 @@ class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
     }
     task_runner_->PostTask(
         FROM_HERE,
-        base::Bind(&LocalWriteClosure::callBlobCallbackOnIDBTaskRunner,
-                   this,
-                   write_status == FileWriterDelegate::SUCCESS_COMPLETED));
+        base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
+                       ReportWriteCompletion,
+                   chained_blob_writer_,
+                   write_status == FileWriterDelegate::SUCCESS_COMPLETED,
+                   bytes_written_));
   }
 
   void writeBlobToFileOnIOThread(const FilePath& file_path,
@@ -2291,16 +2332,21 @@ class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
   }
 
  private:
-  virtual ~LocalWriteClosure() {}
-  friend class base::RefCounted<LocalWriteClosure>;
-
-  void callBlobCallbackOnIDBTaskRunner(bool succeeded) {
-    DCHECK(task_runner_->RunsTasksOnCurrentThread());
-    chained_blob_writer_->ReportWriteCompletion(succeeded, bytes_written_);
+  virtual ~LocalWriteClosure() {
+    // Make sure the last reference to a ChainedBlobWriter is released (and
+    // deleted) on the IDB thread since it owns a transaction which has thread
+    // affinity.
+    IndexedDBBackingStore::Transaction::ChainedBlobWriter* raw_tmp =
+        chained_blob_writer_.get();
+    raw_tmp->AddRef();
+    chained_blob_writer_ = NULL;
+    task_runner_->ReleaseSoon(FROM_HERE, raw_tmp);
   }
+  friend class base::RefCountedThreadSafe<LocalWriteClosure>;
 
-  IndexedDBBackingStore::Transaction::ChainedBlobWriter* chained_blob_writer_;
-  base::TaskRunner* task_runner_;
+  scoped_refptr<IndexedDBBackingStore::Transaction::ChainedBlobWriter>
+      chained_blob_writer_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
   int64 bytes_written_;
 
   DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
@@ -2797,21 +2843,21 @@ leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
                                            const std::string& target,
                                            std::string* found_key,
-                                           leveldb::Status& s) {
+                                           leveldb::Status* s) {
   scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
-  s = it->Seek(target);
-  if (!s.ok())
+  *s = it->Seek(target);
+  if (!s->ok())
     return false;
 
   if (!it->IsValid()) {
-    s = it->SeekToLast();
-    if (!s.ok() || !it->IsValid())
+    *s = it->SeekToLast();
+    if (!s->ok() || !it->IsValid())
       return false;
   }
 
   while (CompareIndexKeys(it->Key(), target) > 0) {
-    s = it->Prev();
-    if (!s.ok() || !it->IsValid())
+    *s = it->Prev();
+    if (!s->ok() || !it->IsValid())
       return false;
   }
 
@@ -2819,8 +2865,8 @@ static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
     *found_key = it->Key().as_string();
 
     // There can be several index keys that compare equal. We want the last one.
-    s = it->Next();
-  } while (s.ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
+    *s = it->Next();
+  } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
 
   return true;
 }
@@ -3049,19 +3095,20 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
   // TODO(alecflett): avoid a copy here?
   IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
 
-  bool first_iteration = true;
-
   // When iterating with PrevNoDuplicate, spec requires that the
   // value we yield for each key is the first duplicate in forwards
   // order.
   IndexedDBKey last_duplicate_key;
 
   bool forward = cursor_options_.forward;
+  bool first_iteration_forward = forward;
+  bool flipped = false;
 
   for (;;) {
     if (next_state == SEEK) {
       // TODO(jsbell): Optimize seeking for reverse cursors as well.
-      if (first_iteration && key && forward) {
+      if (first_iteration_forward && key) {
+        first_iteration_forward = false;
         std::string leveldb_key;
         if (primary_key) {
           leveldb_key = EncodeKey(*key, *primary_key);
@@ -3069,7 +3116,6 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
           leveldb_key = EncodeKey(*key);
         }
         *s = iterator_->Seek(leveldb_key);
-        first_iteration = false;
       } else if (forward) {
         *s = iterator_->Next();
       } else {
@@ -3086,6 +3132,7 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
         // We need to walk forward because we hit the end of
         // the data.
         forward = true;
+        flipped = true;
         continue;
       }
 
@@ -3097,6 +3144,7 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
         // We need to walk forward because now we're beyond the
         // bounds defined by the cursor.
         forward = true;
+        flipped = true;
         continue;
       }
 
@@ -3116,7 +3164,7 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
         if (primary_key && current_key_->Equals(*key) &&
             this->primary_key().IsLessThan(*primary_key))
           continue;
-        if (current_key_->IsLessThan(*key))
+        if (!flipped && current_key_->IsLessThan(*key))
           continue;
       } else {
         if (primary_key && key->Equals(*current_key_) &&
@@ -3145,6 +3193,7 @@ bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
         // between key ranges.
         if (!last_duplicate_key.Equals(*current_key_)) {
           forward = true;
+          flipped = true;
           continue;
         }
 
@@ -3573,7 +3622,7 @@ bool ObjectStoreCursorOptions(
     int64 database_id,
     int64 object_store_id,
     const IndexedDBKeyRange& range,
-    indexed_db::CursorDirection direction,
+    blink::WebIDBCursorDirection direction,
     IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
   cursor_options->database_id = database_id;
   cursor_options->object_store_id = object_store_id;
@@ -3581,10 +3630,11 @@ bool ObjectStoreCursorOptions(
   bool lower_bound = range.lower().IsValid();
   bool upper_bound = range.upper().IsValid();
   cursor_options->forward =
-      (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
-       direction == indexed_db::CURSOR_NEXT);
-  cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
-                            direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
+      (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
+       direction == blink::WebIDBCursorDirectionNext);
+  cursor_options->unique =
+      (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
+       direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
 
   if (!lower_bound) {
     cursor_options->low_key =
@@ -3610,7 +3660,7 @@ bool ObjectStoreCursorOptions(
       if (!FindGreatestKeyLessThanOrEqual(transaction,
                                           cursor_options->high_key,
                                           &cursor_options->high_key,
-                                          s))
+                                          &s))
         return false;
       cursor_options->high_open = false;
     }
@@ -3624,7 +3674,7 @@ bool ObjectStoreCursorOptions(
       std::string found_high_key;
       // TODO(cmumford): Handle this error (crbug.com/363397)
       if (!FindGreatestKeyLessThanOrEqual(
-              transaction, cursor_options->high_key, &found_high_key, s))
+              transaction, cursor_options->high_key, &found_high_key, &s))
         return false;
 
       // If the target key should not be included, but we end up with a smaller
@@ -3646,7 +3696,7 @@ bool IndexCursorOptions(
     int64 object_store_id,
     int64 index_id,
     const IndexedDBKeyRange& range,
-    indexed_db::CursorDirection direction,
+    blink::WebIDBCursorDirection direction,
     IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
   DCHECK(transaction);
   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
@@ -3659,10 +3709,11 @@ bool IndexCursorOptions(
   bool lower_bound = range.lower().IsValid();
   bool upper_bound = range.upper().IsValid();
   cursor_options->forward =
-      (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
-       direction == indexed_db::CURSOR_NEXT);
-  cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
-                            direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
+      (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
+       direction == blink::WebIDBCursorDirectionNext);
+  cursor_options->unique =
+      (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
+       direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
 
   if (!lower_bound) {
     cursor_options->low_key =
@@ -3685,7 +3736,7 @@ bool IndexCursorOptions(
       if (!FindGreatestKeyLessThanOrEqual(transaction,
                                           cursor_options->high_key,
                                           &cursor_options->high_key,
-                                          s))
+                                          &s))
         return false;
       cursor_options->high_open = false;
     }
@@ -3698,7 +3749,7 @@ bool IndexCursorOptions(
     // Seek to the *last* key in the set of non-unique keys
     // TODO(cmumford): Handle this error (crbug.com/363397)
     if (!FindGreatestKeyLessThanOrEqual(
-            transaction, cursor_options->high_key, &found_high_key, s))
+            transaction, cursor_options->high_key, &found_high_key, &s))
       return false;
 
     // If the target key should not be included, but we end up with a smaller
@@ -3719,7 +3770,7 @@ IndexedDBBackingStore::OpenObjectStoreCursor(
     int64 database_id,
     int64 object_store_id,
     const IndexedDBKeyRange& range,
-    indexed_db::CursorDirection direction,
+    blink::WebIDBCursorDirection direction,
     leveldb::Status* s) {
   IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
   *s = leveldb::Status::OK();
@@ -3746,7 +3797,7 @@ IndexedDBBackingStore::OpenObjectStoreKeyCursor(
     int64 database_id,
     int64 object_store_id,
     const IndexedDBKeyRange& range,
-    indexed_db::CursorDirection direction,
+    blink::WebIDBCursorDirection direction,
     leveldb::Status* s) {
   IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
   *s = leveldb::Status::OK();
@@ -3774,7 +3825,7 @@ IndexedDBBackingStore::OpenIndexKeyCursor(
     int64 object_store_id,
     int64 index_id,
     const IndexedDBKeyRange& range,
-    indexed_db::CursorDirection direction,
+    blink::WebIDBCursorDirection direction,
     leveldb::Status* s) {
   IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
   *s = leveldb::Status::OK();
@@ -3803,7 +3854,7 @@ IndexedDBBackingStore::OpenIndexCursor(
     int64 object_store_id,
     int64 index_id,
     const IndexedDBKeyRange& range,
-    indexed_db::CursorDirection direction,
+    blink::WebIDBCursorDirection direction,
     leveldb::Status* s) {
   IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
   LevelDBTransaction* leveldb_transaction = transaction->transaction();
@@ -3997,7 +4048,7 @@ leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
 
   s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
   if (!s.ok()) {
-    INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+    INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
     transaction_ = NULL;
     return s;
   }
@@ -4022,7 +4073,7 @@ leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
   if (new_files_to_write.size()) {
     // This kicks off the writes of the new blobs, if any.
     // This call will zero out new_blob_entries and new_files_to_write.
-    WriteNewBlobs(new_blob_entries, new_files_to_write, callback);
+    WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
     // Remove the add journal, if any; once the blobs are written, and we
     // commit, this will do the cleanup.
     ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode());
@@ -4065,7 +4116,7 @@ leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
     }
   }
   if (!s.ok())
-    INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
+    INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
   else if (blobs_to_remove_.size())
     s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
 
@@ -4096,14 +4147,14 @@ class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
 };
 
 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
-    BlobEntryKeyValuePairVec& new_blob_entries,
-    WriteDescriptorVec& new_files_to_write,
+    BlobEntryKeyValuePairVec* new_blob_entries,
+    WriteDescriptorVec* new_files_to_write,
     scoped_refptr<BlobWriteCallback> callback) {
-  DCHECK_GT(new_files_to_write.size(), 0UL);
+  DCHECK_GT(new_files_to_write->size(), 0UL);
   DCHECK_GT(database_id_, 0);
   BlobEntryKeyValuePairVec::iterator blob_entry_iter;
-  for (blob_entry_iter = new_blob_entries.begin();
-       blob_entry_iter != new_blob_entries.end();
+  for (blob_entry_iter = new_blob_entries->begin();
+       blob_entry_iter != new_blob_entries->end();
        ++blob_entry_iter) {
     // Add the new blob-table entry for each blob to the main transaction, or
     // remove any entry that may exist if there's no new one.
@@ -4123,11 +4174,12 @@ void IndexedDBBackingStore::Transaction::WriteNewBlobs(
 
 void IndexedDBBackingStore::Transaction::Rollback() {
   IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
-  DCHECK(transaction_.get());
   if (chained_blob_writer_) {
     chained_blob_writer_->Abort();
     chained_blob_writer_ = NULL;
   }
+  if (transaction_.get() == NULL)
+    return;
   transaction_->Rollback();
   transaction_ = NULL;
 }