// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/indexed_db/indexed_db_callbacks.h"

#include <algorithm>

#include "base/guid.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/fileapi/fileapi_message_filter.h"
#include "content/browser/indexed_db/indexed_db_blob_info.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_cursor.h"
#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_metadata.h"
#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/common/indexed_db/indexed_db_constants.h"
#include "content/common/indexed_db/indexed_db_messages.h"
#include "webkit/browser/blob/blob_storage_context.h"
#include "webkit/browser/quota/quota_manager.h"
#include "webkit/common/blob/blob_data.h"
#include "webkit/common/blob/shareable_file_reference.h"

using webkit_blob::ShareableFileReference;

namespace content {

namespace {
const int32 kNoCursor = -1;
const int32 kNoDatabaseCallbacks = -1;
const int64 kNoTransaction = -1;
}

IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
                                       int32 ipc_thread_id,
                                       int32 ipc_callbacks_id)
    : dispatcher_host_(dispatcher_host),
      ipc_callbacks_id_(ipc_callbacks_id),
      ipc_thread_id_(ipc_thread_id),
      ipc_cursor_id_(kNoCursor),
      host_transaction_id_(kNoTransaction),
      ipc_database_id_(kNoDatabase),
      ipc_database_callbacks_id_(kNoDatabaseCallbacks),
      data_loss_(blink::WebIDBDataLossNone),
      sent_blocked_(false) {
}

IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
                                       int32 ipc_thread_id,
                                       int32 ipc_callbacks_id,
                                       int32 ipc_cursor_id)
    : dispatcher_host_(dispatcher_host),
      ipc_callbacks_id_(ipc_callbacks_id),
      ipc_thread_id_(ipc_thread_id),
      ipc_cursor_id_(ipc_cursor_id),
      host_transaction_id_(kNoTransaction),
      ipc_database_id_(kNoDatabase),
      ipc_database_callbacks_id_(kNoDatabaseCallbacks),
      data_loss_(blink::WebIDBDataLossNone),
      sent_blocked_(false) {
}

IndexedDBCallbacks::IndexedDBCallbacks(IndexedDBDispatcherHost* dispatcher_host,
                                       int32 ipc_thread_id,
                                       int32 ipc_callbacks_id,
                                       int32 ipc_database_callbacks_id,
                                       int64 host_transaction_id,
                                       const GURL& origin_url)
    : dispatcher_host_(dispatcher_host),
      ipc_callbacks_id_(ipc_callbacks_id),
      ipc_thread_id_(ipc_thread_id),
      ipc_cursor_id_(kNoCursor),
      host_transaction_id_(host_transaction_id),
      origin_url_(origin_url),
      ipc_database_id_(kNoDatabase),
      ipc_database_callbacks_id_(ipc_database_callbacks_id),
      data_loss_(blink::WebIDBDataLossNone),
      sent_blocked_(false) {
}

IndexedDBCallbacks::~IndexedDBCallbacks() {}

void IndexedDBCallbacks::OnError(const IndexedDBDatabaseError& error) {
  DCHECK(dispatcher_host_.get());

  dispatcher_host_->Send(new IndexedDBMsg_CallbacksError(
      ipc_thread_id_, ipc_callbacks_id_, error.code(), error.message()));
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccess(const std::vector<base::string16>& value) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  std::vector<base::string16> list;
  for (unsigned i = 0; i < value.size(); ++i)
    list.push_back(value[i]);

  dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessStringList(
      ipc_thread_id_, ipc_callbacks_id_, list));
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnBlocked(int64 existing_version) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  // No transaction/db callbacks for DeleteDatabase.
  DCHECK_EQ(kNoTransaction == host_transaction_id_,
            kNoDatabaseCallbacks == ipc_database_callbacks_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);

  if (sent_blocked_)
    return;

  sent_blocked_ = true;
  dispatcher_host_->Send(new IndexedDBMsg_CallbacksIntBlocked(
      ipc_thread_id_, ipc_callbacks_id_, existing_version));
}

void IndexedDBCallbacks::OnDataLoss(blink::WebIDBDataLoss data_loss,
                                    std::string data_loss_message) {
  DCHECK_NE(blink::WebIDBDataLossNone, data_loss);
  data_loss_ = data_loss;
  data_loss_message_ = data_loss_message;
}

void IndexedDBCallbacks::OnUpgradeNeeded(
    int64 old_version,
    scoped_ptr<IndexedDBConnection> connection,
    const IndexedDBDatabaseMetadata& metadata) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_NE(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_NE(kNoDatabaseCallbacks, ipc_database_callbacks_id_);

  dispatcher_host_->RegisterTransactionId(host_transaction_id_, origin_url_);
  int32 ipc_database_id =
      dispatcher_host_->Add(connection.release(), ipc_thread_id_, origin_url_);
  if (ipc_database_id < 0)
    return;
  ipc_database_id_ = ipc_database_id;
  IndexedDBMsg_CallbacksUpgradeNeeded_Params params;
  params.ipc_thread_id = ipc_thread_id_;
  params.ipc_callbacks_id = ipc_callbacks_id_;
  params.ipc_database_id = ipc_database_id;
  params.ipc_database_callbacks_id = ipc_database_callbacks_id_;
  params.old_version = old_version;
  params.idb_metadata = IndexedDBDispatcherHost::ConvertMetadata(metadata);
  params.data_loss = data_loss_;
  params.data_loss_message = data_loss_message_;
  dispatcher_host_->Send(new IndexedDBMsg_CallbacksUpgradeNeeded(params));
}

void IndexedDBCallbacks::OnSuccess(scoped_ptr<IndexedDBConnection> connection,
                                   const IndexedDBDatabaseMetadata& metadata) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_NE(kNoTransaction, host_transaction_id_);
  DCHECK_NE(ipc_database_id_ == kNoDatabase, !connection);
  DCHECK_NE(kNoDatabaseCallbacks, ipc_database_callbacks_id_);

  scoped_refptr<IndexedDBCallbacks> self(this);

  int32 ipc_object_id = kNoDatabase;
  // Only register if the connection was not previously sent in OnUpgradeNeeded.
  if (ipc_database_id_ == kNoDatabase) {
    ipc_object_id = dispatcher_host_->Add(
        connection.release(), ipc_thread_id_, origin_url_);
  }

  dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessIDBDatabase(
      ipc_thread_id_,
      ipc_callbacks_id_,
      ipc_database_callbacks_id_,
      ipc_object_id,
      IndexedDBDispatcherHost::ConvertMetadata(metadata)));
  dispatcher_host_ = NULL;
}

static std::string CreateBlobData(
    const IndexedDBBlobInfo& blob_info,
    scoped_refptr<IndexedDBDispatcherHost> dispatcher_host,
    webkit_blob::BlobStorageContext* blob_storage_context,
    base::TaskRunner* task_runner) {
  std::string uuid = blob_info.uuid();
  if (!uuid.empty()) {
    // We're sending back a live blob, not a reference into our backing store.
    scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle(
        blob_storage_context->GetBlobDataFromUUID(uuid));
    dispatcher_host->HoldBlobDataHandle(uuid, blob_data_handle.Pass());
    return uuid;
  }
  scoped_refptr<ShareableFileReference> shareable_file =
      ShareableFileReference::Get(blob_info.file_path());
  if (!shareable_file.get()) {
    shareable_file = ShareableFileReference::GetOrCreate(
        blob_info.file_path(),
        ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE,
        task_runner);
    if (!blob_info.release_callback().is_null())
      shareable_file->AddFinalReleaseCallback(blob_info.release_callback());
  }

  uuid = base::GenerateGUID();
  scoped_refptr<webkit_blob::BlobData> blob_data =
      new webkit_blob::BlobData(uuid);
  blob_data->set_content_type(base::UTF16ToUTF8(blob_info.type()));
  blob_data->AppendFile(
      blob_info.file_path(), 0, blob_info.size(), blob_info.last_modified());
  scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle(
      blob_storage_context->AddFinishedBlob(blob_data.get()));
  dispatcher_host->HoldBlobDataHandle(uuid, blob_data_handle.Pass());

  return uuid;
}

static bool CreateAllBlobs(
    const std::vector<IndexedDBBlobInfo>& blob_info,
    std::vector<IndexedDBMsg_BlobOrFileInfo>* blob_or_file_info,
    scoped_refptr<IndexedDBDispatcherHost> dispatcher_host) {
  DCHECK_EQ(blob_info.size(), blob_or_file_info->size());
  size_t i;
  if (!dispatcher_host->blob_storage_context())
    return false;
  for (i = 0; i < blob_info.size(); ++i) {
    (*blob_or_file_info)[i].uuid =
        CreateBlobData(blob_info[i],
                       dispatcher_host,
                       dispatcher_host->blob_storage_context(),
                       dispatcher_host->Context()->TaskRunner());
  }
  return true;
}

template <class ParamType, class MsgType>
static void CreateBlobsAndSend(
    ParamType* params,
    scoped_refptr<IndexedDBDispatcherHost> dispatcher_host,
    const std::vector<IndexedDBBlobInfo>& blob_info,
    std::vector<IndexedDBMsg_BlobOrFileInfo>* blob_or_file_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  if (CreateAllBlobs(blob_info, blob_or_file_info, dispatcher_host))
    dispatcher_host->Send(new MsgType(*params));
}

static void BlobLookupForCursorPrefetch(
    IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params* params,
    scoped_refptr<IndexedDBDispatcherHost> dispatcher_host,
    const std::vector<IndexedDBValue>& values) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK_EQ(values.size(), params->blob_or_file_infos.size());

  std::vector<IndexedDBValue>::const_iterator value_iter;
  std::vector<std::vector<IndexedDBMsg_BlobOrFileInfo> >::iterator blob_iter;
  for (value_iter = values.begin(), blob_iter =
       params->blob_or_file_infos.begin(); value_iter != values.end();
       ++value_iter, ++blob_iter) {
    if (!CreateAllBlobs(value_iter->blob_info, &*blob_iter, dispatcher_host))
      return;
  }
  dispatcher_host->Send(
      new IndexedDBMsg_CallbacksSuccessCursorPrefetch(*params));
}

static void FillInBlobData(
    const std::vector<IndexedDBBlobInfo>& blob_info,
    std::vector<IndexedDBMsg_BlobOrFileInfo>* blob_or_file_info) {
  for (std::vector<IndexedDBBlobInfo>::const_iterator iter = blob_info.begin();
       iter != blob_info.end();
       ++iter) {
    if (iter->is_file()) {
      IndexedDBMsg_BlobOrFileInfo info;
      info.is_file = true;
      info.mime_type = iter->type();
      info.file_name = iter->file_name();
      info.file_path = iter->file_path().AsUTF16Unsafe();
      info.size = iter->size();
      info.last_modified = iter->last_modified().ToDoubleT();
      blob_or_file_info->push_back(info);
    } else {
      IndexedDBMsg_BlobOrFileInfo info;
      info.mime_type = iter->type();
      info.size = iter->size();
      blob_or_file_info->push_back(info);
    }
  }
}

void IndexedDBCallbacks::RegisterBlobsAndSend(
    const std::vector<IndexedDBBlobInfo>& blob_info,
    const base::Closure& callback) {
  std::vector<IndexedDBBlobInfo>::const_iterator iter;
  for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) {
    if (!iter->mark_used_callback().is_null())
      iter->mark_used_callback().Run();
  }
  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, callback);
}

void IndexedDBCallbacks::OnSuccess(scoped_refptr<IndexedDBCursor> cursor,
                                   const IndexedDBKey& key,
                                   const IndexedDBKey& primary_key,
                                   IndexedDBValue* value) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  int32 ipc_object_id = dispatcher_host_->Add(cursor.get());
  scoped_ptr<IndexedDBMsg_CallbacksSuccessIDBCursor_Params> params(
      new IndexedDBMsg_CallbacksSuccessIDBCursor_Params());
  params->ipc_thread_id = ipc_thread_id_;
  params->ipc_callbacks_id = ipc_callbacks_id_;
  params->ipc_cursor_id = ipc_object_id;
  params->key = key;
  params->primary_key = primary_key;
  if (value && !value->empty())
    std::swap(params->value, value->bits);
  // TODO(alecflett): Avoid a copy here: the whole params object is
  // being copied into the message.
  if (!value || value->blob_info.empty()) {
    dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessIDBCursor(*params));
  } else {
    IndexedDBMsg_CallbacksSuccessIDBCursor_Params* p = params.get();
    FillInBlobData(value->blob_info, &p->blob_or_file_info);
    RegisterBlobsAndSend(
        value->blob_info,
        base::Bind(
            CreateBlobsAndSend<IndexedDBMsg_CallbacksSuccessIDBCursor_Params,
                               IndexedDBMsg_CallbacksSuccessIDBCursor>,
            base::Owned(params.release()),
            dispatcher_host_,
            value->blob_info,
            base::Unretained(&p->blob_or_file_info)));
  }
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccess(const IndexedDBKey& key,
                                   const IndexedDBKey& primary_key,
                                   IndexedDBValue* value) {
  DCHECK(dispatcher_host_.get());

  DCHECK_NE(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  IndexedDBCursor* idb_cursor =
      dispatcher_host_->GetCursorFromId(ipc_cursor_id_);

  DCHECK(idb_cursor);
  if (!idb_cursor)
    return;

  scoped_ptr<IndexedDBMsg_CallbacksSuccessCursorContinue_Params> params(
      new IndexedDBMsg_CallbacksSuccessCursorContinue_Params());
  params->ipc_thread_id = ipc_thread_id_;
  params->ipc_callbacks_id = ipc_callbacks_id_;
  params->ipc_cursor_id = ipc_cursor_id_;
  params->key = key;
  params->primary_key = primary_key;
  if (value && !value->empty())
    std::swap(params->value, value->bits);
  // TODO(alecflett): Avoid a copy here: the whole params object is
  // being copied into the message.
  if (!value || value->blob_info.empty()) {
    dispatcher_host_->Send(
        new IndexedDBMsg_CallbacksSuccessCursorContinue(*params));
  } else {
    IndexedDBMsg_CallbacksSuccessCursorContinue_Params* p = params.get();
    FillInBlobData(value->blob_info, &p->blob_or_file_info);
    RegisterBlobsAndSend(
        value->blob_info,
        base::Bind(CreateBlobsAndSend<
                       IndexedDBMsg_CallbacksSuccessCursorContinue_Params,
                       IndexedDBMsg_CallbacksSuccessCursorContinue>,
                   base::Owned(params.release()),
                   dispatcher_host_,
                   value->blob_info,
                   base::Unretained(&p->blob_or_file_info)));
  }
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccessWithPrefetch(
    const std::vector<IndexedDBKey>& keys,
    const std::vector<IndexedDBKey>& primary_keys,
    std::vector<IndexedDBValue>* values) {
  DCHECK_EQ(keys.size(), primary_keys.size());
  DCHECK_EQ(keys.size(), values->size());

  DCHECK(dispatcher_host_.get());

  DCHECK_NE(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  std::vector<IndexedDBKey> msgKeys;
  std::vector<IndexedDBKey> msgPrimaryKeys;

  for (size_t i = 0; i < keys.size(); ++i) {
    msgKeys.push_back(keys[i]);
    msgPrimaryKeys.push_back(primary_keys[i]);
  }

  scoped_ptr<IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params> params(
      new IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params());
  params->ipc_thread_id = ipc_thread_id_;
  params->ipc_callbacks_id = ipc_callbacks_id_;
  params->ipc_cursor_id = ipc_cursor_id_;
  params->keys = msgKeys;
  params->primary_keys = msgPrimaryKeys;
  std::vector<std::string>& values_bits = params->values;
  values_bits.resize(values->size());
  std::vector<std::vector<IndexedDBMsg_BlobOrFileInfo> >& values_blob_infos =
      params->blob_or_file_infos;
  values_blob_infos.resize(values->size());

  bool found_blob_info = false;
  std::vector<IndexedDBValue>::iterator iter = values->begin();
  for (size_t i = 0; iter != values->end(); ++iter, ++i) {
    values_bits[i].swap(iter->bits);
    if (iter->blob_info.size()) {
      found_blob_info = true;
      FillInBlobData(iter->blob_info, &values_blob_infos[i]);
      std::vector<IndexedDBBlobInfo>::const_iterator blob_iter;
      for (blob_iter = iter->blob_info.begin();
           blob_iter != iter->blob_info.end();
           ++blob_iter) {
        if (!blob_iter->mark_used_callback().is_null())
          blob_iter->mark_used_callback().Run();
      }
    }
  }

  if (found_blob_info) {
    BrowserThread::PostTask(BrowserThread::IO,
                            FROM_HERE,
                            base::Bind(BlobLookupForCursorPrefetch,
                                       base::Owned(params.release()),
                                       dispatcher_host_,
                                       *values));
  } else {
    dispatcher_host_->Send(
        new IndexedDBMsg_CallbacksSuccessCursorPrefetch(*params.get()));
  }
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccess(IndexedDBValue* value,
                                   const IndexedDBKey& key,
                                   const IndexedDBKeyPath& key_path) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  scoped_ptr<IndexedDBMsg_CallbacksSuccessValueWithKey_Params> params(
      new IndexedDBMsg_CallbacksSuccessValueWithKey_Params());
  params->ipc_thread_id = ipc_thread_id_;
  params->ipc_callbacks_id = ipc_callbacks_id_;
  params->primary_key = key;
  params->key_path = key_path;
  if (value && !value->empty())
    std::swap(params->value, value->bits);
  if (!value || value->blob_info.empty()) {
    dispatcher_host_->Send(
        new IndexedDBMsg_CallbacksSuccessValueWithKey(*params));
  } else {
    IndexedDBMsg_CallbacksSuccessValueWithKey_Params* p = params.get();
    FillInBlobData(value->blob_info, &p->blob_or_file_info);
    RegisterBlobsAndSend(
        value->blob_info,
        base::Bind(
            CreateBlobsAndSend<IndexedDBMsg_CallbacksSuccessValueWithKey_Params,
                               IndexedDBMsg_CallbacksSuccessValueWithKey>,
            base::Owned(params.release()),
            dispatcher_host_,
            value->blob_info,
            base::Unretained(&p->blob_or_file_info)));
  }
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccess(IndexedDBValue* value) {
  DCHECK(dispatcher_host_.get());
  DCHECK(kNoCursor == ipc_cursor_id_ || value == NULL);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  scoped_ptr<IndexedDBMsg_CallbacksSuccessValue_Params> params(
      new IndexedDBMsg_CallbacksSuccessValue_Params());
  params->ipc_thread_id = ipc_thread_id_;
  params->ipc_callbacks_id = ipc_callbacks_id_;
  if (value && !value->empty())
    std::swap(params->value, value->bits);
  if (!value || value->blob_info.empty()) {
    dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessValue(*params));
  } else {
    IndexedDBMsg_CallbacksSuccessValue_Params* p = params.get();
    FillInBlobData(value->blob_info, &p->blob_or_file_info);
    RegisterBlobsAndSend(
        value->blob_info,
        base::Bind(CreateBlobsAndSend<IndexedDBMsg_CallbacksSuccessValue_Params,
                                      IndexedDBMsg_CallbacksSuccessValue>,
                   base::Owned(params.release()),
                   dispatcher_host_,
                   value->blob_info,
                   base::Unretained(&p->blob_or_file_info)));
  }
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccess(const IndexedDBKey& value) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessIndexedDBKey(
      ipc_thread_id_, ipc_callbacks_id_, value));
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccess(int64 value) {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessInteger(
      ipc_thread_id_, ipc_callbacks_id_, value));
  dispatcher_host_ = NULL;
}

void IndexedDBCallbacks::OnSuccess() {
  DCHECK(dispatcher_host_.get());

  DCHECK_EQ(kNoCursor, ipc_cursor_id_);
  DCHECK_EQ(kNoTransaction, host_transaction_id_);
  DCHECK_EQ(kNoDatabase, ipc_database_id_);
  DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_);
  DCHECK_EQ(blink::WebIDBDataLossNone, data_loss_);

  dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessUndefined(
      ipc_thread_id_, ipc_callbacks_id_));
  dispatcher_host_ = NULL;
}

}  // namespace content
