| Index: content/browser/indexed_db/indexed_db_transaction.cc
 | 
| diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
 | 
| index 28f8378f54dfc022e126cbb8897491509dca52c1..74b44ad0c910739a77a97e9c60c5848e962705b0 100644
 | 
| --- a/content/browser/indexed_db/indexed_db_transaction.cc
 | 
| +++ b/content/browser/indexed_db/indexed_db_transaction.cc
 | 
| @@ -60,6 +60,7 @@ IndexedDBTransaction::IndexedDBTransaction(
 | 
|        used_(false),
 | 
|        state_(CREATED),
 | 
|        commit_pending_(false),
 | 
| +      blob_write_success_(false),
 | 
|        callbacks_(callbacks),
 | 
|        database_(database),
 | 
|        transaction_(database->backing_store()),
 | 
| @@ -95,7 +96,7 @@ void IndexedDBTransaction::ScheduleTask(Operation task, Operation abort_task) {
 | 
|  
 | 
|  void IndexedDBTransaction::ScheduleTask(IndexedDBDatabase::TaskType type,
 | 
|                                          Operation task) {
 | 
| -  if (state_ == FINISHED)
 | 
| +  if (IsStatePastRunning())
 | 
|      return;
 | 
|  
 | 
|    used_ = true;
 | 
| @@ -206,13 +207,30 @@ void IndexedDBTransaction::Start() {
 | 
|    RunTasksIfStarted();
 | 
|  }
 | 
|  
 | 
| +class BlobWriteCallbackImpl : public IndexedDBBackingStore::BlobWriteCallback {
 | 
| + public:
 | 
| +  BlobWriteCallbackImpl(scoped_refptr<IndexedDBTransaction> transaction)
 | 
| +    : transaction_(transaction) {
 | 
| +  }
 | 
| +  virtual ~BlobWriteCallbackImpl() {
 | 
| +  }
 | 
| +  virtual void didSucceed() {
 | 
| +    transaction_->BlobWriteComplete(true);
 | 
| +  }
 | 
| +  virtual void didFail() {
 | 
| +    transaction_->BlobWriteComplete(false);
 | 
| +  }
 | 
| + private:
 | 
| +  scoped_refptr<IndexedDBTransaction> transaction_;
 | 
| +};
 | 
| +
 | 
|  void IndexedDBTransaction::Commit() {
 | 
|    IDB_TRACE("IndexedDBTransaction::Commit");
 | 
|  
 | 
|    // In multiprocess ports, front-end may have requested a commit but
 | 
|    // an abort has already been initiated asynchronously by the
 | 
|    // back-end.
 | 
| -  if (state_ == FINISHED)
 | 
| +  if (IsStatePastRunning())
 | 
|      return;
 | 
|  
 | 
|    DCHECK(!used_ || state_ == STARTED);
 | 
| @@ -224,44 +242,18 @@ void IndexedDBTransaction::Commit() {
 | 
|    if (HasPendingTasks())
 | 
|      return;
 | 
|  
 | 
| -  // The last reference to this object may be released while performing the
 | 
| -  // commit steps below. We therefore take a self reference to keep ourselves
 | 
| -  // alive while executing this method.
 | 
| -  scoped_refptr<IndexedDBTransaction> protect(this);
 | 
| -
 | 
| -  // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843
 | 
| -  abort_task_stack_.clear();
 | 
| -
 | 
| -  state_ = FINISHED;
 | 
| -
 | 
| -  bool committed = !used_ || transaction_.Commit();
 | 
| -
 | 
| -  // Backing store resources (held via cursors) must be released
 | 
| -  // before script callbacks are fired, as the script callbacks may
 | 
| -  // release references and allow the backing store itself to be
 | 
| -  // released, and order is critical.
 | 
| -  CloseOpenCursors();
 | 
| -  transaction_.Reset();
 | 
| -
 | 
| -  // Transactions must also be marked as completed before the
 | 
| -  // front-end is notified, as the transaction completion unblocks
 | 
| -  // operations like closing connections.
 | 
| -  database_->transaction_coordinator().DidFinishTransaction(this);
 | 
| -  database_->TransactionFinished(this);
 | 
| +  state_ = COMMITTING;
 | 
|  
 | 
| -  if (committed) {
 | 
| -    callbacks_->OnComplete(id_);
 | 
| -    database_->TransactionFinishedAndCompleteFired(this);
 | 
| -  } else {
 | 
| -    callbacks_->OnAbort(
 | 
| -        id_,
 | 
| -        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
 | 
| -                               "Internal error committing transaction."));
 | 
| -    database_->TransactionFinishedAndAbortFired(this);
 | 
| -    database_->TransactionCommitFailed();
 | 
| +  if (!used_)
 | 
| +    CommitPhaseTwo(WAS_NOT_USED, DO_COMMIT);
 | 
| +  else {
 | 
| +    scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback(
 | 
| +        new BlobWriteCallbackImpl(this));
 | 
| +    // CommitPhaseOne will call the callback synchronously if there are no blobs
 | 
| +    // to write.
 | 
| +    if (!transaction_.CommitPhaseOne(callback))
 | 
| +      CommitPhaseTwo(WAS_USED, SKIP_COMMIT);
 | 
|    }
 | 
| -
 | 
| -  database_ = NULL;
 | 
|  }
 | 
|  
 | 
|  void IndexedDBTransaction::ProcessTaskQueue() {
 | 
| @@ -286,8 +278,8 @@ void IndexedDBTransaction::ProcessTaskQueue() {
 | 
|  
 | 
|    TaskQueue* task_queue =
 | 
|        pending_preemptive_events_ ? &preemptive_task_queue_ : &task_queue_;
 | 
| -  while (!task_queue->empty() && state_ != FINISHED) {
 | 
| -    DCHECK_EQ(STARTED, state_);
 | 
| +  while (!task_queue->empty() && !IsStatePastRunning()) {
 | 
| +    DCHECK_EQ(state_, STARTED);
 | 
|      Operation task(task_queue->pop());
 | 
|      task.Run(this);
 | 
|      if (!pending_preemptive_events_) {
 | 
| @@ -302,10 +294,23 @@ void IndexedDBTransaction::ProcessTaskQueue() {
 | 
|  
 | 
|    // If there are no pending tasks, we haven't already committed/aborted,
 | 
|    // and the front-end requested a commit, it is now safe to do so.
 | 
| -  if (!HasPendingTasks() && state_ != FINISHED && commit_pending_)
 | 
| +  if (!HasPendingTasks() && !IsStatePastRunning() && commit_pending_)
 | 
|      Commit();
 | 
|  }
 | 
|  
 | 
| +void IndexedDBTransaction::BlobWriteComplete(bool success)
 | 
| +{
 | 
| +  IDB_TRACE("IndexedDBTransaction::BlobWriteComplete");
 | 
| +  if (state_ == FINISHED) // aborted
 | 
| +    return;
 | 
| +  DCHECK_EQ(state_, COMMITTING);
 | 
| +  if (success)
 | 
| +    CommitPhaseTwo(WAS_USED, DO_COMMIT);
 | 
| +  else
 | 
| +    Abort(IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionDataError,
 | 
| +                                 "Failed to write blobs"));
 | 
| +}
 | 
| +
 | 
|  void IndexedDBTransaction::CloseOpenCursors() {
 | 
|    for (std::set<IndexedDBCursor*>::iterator i = open_cursors_.begin();
 | 
|         i != open_cursors_.end();
 | 
| @@ -314,4 +319,50 @@ void IndexedDBTransaction::CloseOpenCursors() {
 | 
|    open_cursors_.clear();
 | 
|  }
 | 
|  
 | 
| +void IndexedDBTransaction::CommitPhaseTwo(TransactionUseState use_state,
 | 
| +    ShouldSkipCommit skip_commit) {
 | 
| +  DCHECK_EQ(state_, COMMITTING);
 | 
| +  state_ = FINISHED;
 | 
| +
 | 
| +  // The last reference to this object may be released while performing the
 | 
| +  // commit steps below. We therefore take a self reference to keep ourselves
 | 
| +  // alive while executing this method.
 | 
| +  scoped_refptr<IndexedDBTransaction> protect(this);
 | 
| +
 | 
| +  // TODO(jsbell): Run abort tasks if commit fails? http://crbug.com/241843
 | 
| +  abort_task_stack_.clear();
 | 
| +
 | 
| +  state_ = FINISHED;
 | 
| +
 | 
| +  bool committed = (use_state == WAS_NOT_USED) ||
 | 
| +      ((skip_commit == DO_COMMIT) && transaction_.CommitPhaseTwo());
 | 
| +
 | 
| +  // Backing store resources (held via cursors) must be released
 | 
| +  // before script callbacks are fired, as the script callbacks may
 | 
| +  // release references and allow the backing store itself to be
 | 
| +  // released, and order is critical.
 | 
| +  CloseOpenCursors();
 | 
| +  transaction_.Reset();
 | 
| +
 | 
| +  // Transactions must also be marked as completed before the
 | 
| +  // front-end is notified, as the transaction completion unblocks
 | 
| +  // operations like closing connections.
 | 
| +  database_->transaction_coordinator().DidFinishTransaction(this);
 | 
| +  database_->TransactionFinished(this);
 | 
| +
 | 
| +  if (committed) {
 | 
| +    callbacks_->OnComplete(id_);
 | 
| +    database_->TransactionFinishedAndCompleteFired(this);
 | 
| +  } else {
 | 
| +    callbacks_->OnAbort(
 | 
| +        id_,
 | 
| +        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
 | 
| +                               "Internal error committing transaction."));
 | 
| +    database_->TransactionFinishedAndAbortFired(this);
 | 
| +    database_->TransactionCommitFailed();
 | 
| +  }
 | 
| +
 | 
| +  database_ = NULL;
 | 
| +}
 | 
| +
 | 
|  }  // namespace content
 | 
| 
 |