blob: baa3a69d10c7c2db3fa191be338a6750d9eb33cf [file] [log] [blame]
// 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_dispatcher_host.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/guid.h"
#include "base/memory/scoped_vector.h"
#include "base/process/process.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/indexed_db/indexed_db_callbacks.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_metadata.h"
#include "content/browser/indexed_db/indexed_db_pending_connection.h"
#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/browser/renderer_host/render_message_filter.h"
#include "content/common/indexed_db/indexed_db_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/database/database_util.h"
#include "storage/common/database/database_identifier.h"
#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
#include "url/gurl.h"
using storage::DatabaseUtil;
using blink::WebIDBKey;
namespace content {
IndexedDBDispatcherHost::IndexedDBDispatcherHost(
int ipc_process_id,
net::URLRequestContextGetter* request_context_getter,
IndexedDBContextImpl* indexed_db_context,
ChromeBlobStorageContext* blob_storage_context)
: BrowserMessageFilter(IndexedDBMsgStart),
request_context_getter_(request_context_getter),
request_context_(NULL),
indexed_db_context_(indexed_db_context),
blob_storage_context_(blob_storage_context),
database_dispatcher_host_(new DatabaseDispatcherHost(this)),
cursor_dispatcher_host_(new CursorDispatcherHost(this)),
ipc_process_id_(ipc_process_id) {
DCHECK(indexed_db_context_.get());
}
IndexedDBDispatcherHost::IndexedDBDispatcherHost(
int ipc_process_id,
net::URLRequestContext* request_context,
IndexedDBContextImpl* indexed_db_context,
ChromeBlobStorageContext* blob_storage_context)
: BrowserMessageFilter(IndexedDBMsgStart),
request_context_(request_context),
indexed_db_context_(indexed_db_context),
blob_storage_context_(blob_storage_context),
database_dispatcher_host_(new DatabaseDispatcherHost(this)),
cursor_dispatcher_host_(new CursorDispatcherHost(this)),
ipc_process_id_(ipc_process_id) {
DCHECK(indexed_db_context_.get());
}
IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {
for (auto& iter : blob_data_handle_map_)
delete iter.second.first;
}
void IndexedDBDispatcherHost::OnChannelConnected(int32 peer_pid) {
BrowserMessageFilter::OnChannelConnected(peer_pid);
if (request_context_getter_.get()) {
DCHECK(!request_context_);
request_context_ = request_context_getter_->GetURLRequestContext();
request_context_getter_ = NULL;
DCHECK(request_context_);
}
}
void IndexedDBDispatcherHost::OnChannelClosing() {
bool success = indexed_db_context_->TaskRunner()->PostTask(
FROM_HERE,
base::Bind(&IndexedDBDispatcherHost::ResetDispatcherHosts, this));
if (!success)
ResetDispatcherHosts();
}
void IndexedDBDispatcherHost::OnDestruct() const {
// The last reference to the dispatcher may be a posted task, which would
// be destructed on the IndexedDB thread. Without this override, that would
// take the dispatcher with it. Since the dispatcher may be keeping the
// IndexedDBContext alive, it might be destructed to on its own thread,
// which is not supported. Ensure destruction runs on the IO thread instead.
BrowserThread::DeleteOnIOThread::Destruct(this);
}
void IndexedDBDispatcherHost::ResetDispatcherHosts() {
// It is important that the various *_dispatcher_host_ members are reset
// on the IndexedDB thread, since there might be incoming messages on that
// thread, and we must not reset the dispatcher hosts until after those
// messages are processed.
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
// Note that we explicitly separate CloseAll() from destruction of the
// DatabaseDispatcherHost, since CloseAll() can invoke callbacks which need to
// be dispatched through database_dispatcher_host_.
database_dispatcher_host_->CloseAll();
database_dispatcher_host_.reset();
cursor_dispatcher_host_.reset();
}
base::TaskRunner* IndexedDBDispatcherHost::OverrideTaskRunnerForMessage(
const IPC::Message& message) {
if (IPC_MESSAGE_CLASS(message) != IndexedDBMsgStart)
return NULL;
switch (message.type()) {
case IndexedDBHostMsg_DatabasePut::ID:
case IndexedDBHostMsg_AckReceivedBlobs::ID:
return NULL;
default:
return indexed_db_context_->TaskRunner();
}
}
bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message& message) {
if (IPC_MESSAGE_CLASS(message) != IndexedDBMsgStart)
return false;
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread() ||
(message.type() == IndexedDBHostMsg_DatabasePut::ID ||
message.type() == IndexedDBHostMsg_AckReceivedBlobs::ID));
bool handled = database_dispatcher_host_->OnMessageReceived(message) ||
cursor_dispatcher_host_->OnMessageReceived(message);
if (!handled) {
handled = true;
IPC_BEGIN_MESSAGE_MAP(IndexedDBDispatcherHost, message)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryGetDatabaseNames,
OnIDBFactoryGetDatabaseNames)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryOpen, OnIDBFactoryOpen)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_FactoryDeleteDatabase,
OnIDBFactoryDeleteDatabase)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_AckReceivedBlobs, OnAckReceivedBlobs)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
}
return handled;
}
int32 IndexedDBDispatcherHost::Add(IndexedDBCursor* cursor) {
if (!cursor_dispatcher_host_) {
return 0;
}
return cursor_dispatcher_host_->map_.Add(cursor);
}
int32 IndexedDBDispatcherHost::Add(IndexedDBConnection* connection,
int32 ipc_thread_id,
const GURL& origin_url) {
if (!database_dispatcher_host_) {
connection->Close();
delete connection;
return -1;
}
int32 ipc_database_id = database_dispatcher_host_->map_.Add(connection);
Context()->ConnectionOpened(origin_url, connection);
database_dispatcher_host_->database_url_map_[ipc_database_id] = origin_url;
return ipc_database_id;
}
void IndexedDBDispatcherHost::RegisterTransactionId(int64 host_transaction_id,
const GURL& url) {
if (!database_dispatcher_host_)
return;
database_dispatcher_host_->transaction_url_map_[host_transaction_id] = url;
}
int64 IndexedDBDispatcherHost::HostTransactionId(int64 transaction_id) {
// Inject the renderer process id into the transaction id, to
// uniquely identify this transaction, and effectively bind it to
// the renderer that initiated it. The lower 32 bits of
// transaction_id are guaranteed to be unique within that renderer.
base::ProcessId pid = peer_pid();
DCHECK(!(transaction_id >> 32)) << "Transaction ids can only be 32 bits";
static_assert(sizeof(base::ProcessId) <= sizeof(int32),
"Process ID must fit in 32 bits");
return transaction_id | (static_cast<uint64>(pid) << 32);
}
int64 IndexedDBDispatcherHost::RendererTransactionId(
int64 host_transaction_id) {
DCHECK(host_transaction_id >> 32 == peer_pid())
<< "Invalid renderer target for transaction id";
return host_transaction_id & 0xffffffff;
}
// static
uint32 IndexedDBDispatcherHost::TransactionIdToRendererTransactionId(
int64 host_transaction_id) {
return host_transaction_id & 0xffffffff;
}
// static
uint32 IndexedDBDispatcherHost::TransactionIdToProcessId(
int64 host_transaction_id) {
return (host_transaction_id >> 32) & 0xffffffff;
}
std::string IndexedDBDispatcherHost::HoldBlobData(
const IndexedDBBlobInfo& blob_info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
std::string uuid = blob_info.uuid();
storage::BlobStorageContext* context = blob_storage_context_->context();
scoped_ptr<storage::BlobDataHandle> blob_data_handle;
if (uuid.empty()) {
uuid = base::GenerateGUID();
storage::BlobDataBuilder blob_data_builder(uuid);
blob_data_builder.set_content_type(base::UTF16ToUTF8(blob_info.type()));
blob_data_builder.AppendFile(blob_info.file_path(), 0, blob_info.size(),
blob_info.last_modified());
blob_data_handle = context->AddFinishedBlob(&blob_data_builder);
} else {
auto iter = blob_data_handle_map_.find(uuid);
if (iter != blob_data_handle_map_.end()) {
iter->second.second += 1;
return uuid;
}
blob_data_handle = context->GetBlobDataFromUUID(uuid);
}
DCHECK(!ContainsKey(blob_data_handle_map_, uuid));
blob_data_handle_map_[uuid] = std::make_pair(blob_data_handle.release(), 1);
return uuid;
}
void IndexedDBDispatcherHost::DropBlobData(const std::string& uuid) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
BlobDataHandleMap::iterator iter = blob_data_handle_map_.find(uuid);
if (iter != blob_data_handle_map_.end()) {
DCHECK_GE(iter->second.second, 1);
if (iter->second.second == 1) {
delete iter->second.first;
blob_data_handle_map_.erase(iter);
} else {
iter->second.second -= 1;
}
} else {
DLOG(FATAL) << "Failed to find blob UUID in map:" << uuid;
}
}
IndexedDBCursor* IndexedDBDispatcherHost::GetCursorFromId(int32 ipc_cursor_id) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
return cursor_dispatcher_host_->map_.Lookup(ipc_cursor_id);
}
::IndexedDBDatabaseMetadata IndexedDBDispatcherHost::ConvertMetadata(
const content::IndexedDBDatabaseMetadata& web_metadata) {
::IndexedDBDatabaseMetadata metadata;
metadata.id = web_metadata.id;
metadata.name = web_metadata.name;
metadata.version = web_metadata.version;
metadata.int_version = web_metadata.int_version;
metadata.max_object_store_id = web_metadata.max_object_store_id;
for (const auto& iter : web_metadata.object_stores) {
const content::IndexedDBObjectStoreMetadata& web_store_metadata =
iter.second;
::IndexedDBObjectStoreMetadata idb_store_metadata;
idb_store_metadata.id = web_store_metadata.id;
idb_store_metadata.name = web_store_metadata.name;
idb_store_metadata.key_path = web_store_metadata.key_path;
idb_store_metadata.auto_increment = web_store_metadata.auto_increment;
idb_store_metadata.max_index_id = web_store_metadata.max_index_id;
for (const auto& index_iter : web_store_metadata.indexes) {
const content::IndexedDBIndexMetadata& web_index_metadata =
index_iter.second;
::IndexedDBIndexMetadata idb_index_metadata;
idb_index_metadata.id = web_index_metadata.id;
idb_index_metadata.name = web_index_metadata.name;
idb_index_metadata.key_path = web_index_metadata.key_path;
idb_index_metadata.unique = web_index_metadata.unique;
idb_index_metadata.multi_entry = web_index_metadata.multi_entry;
idb_store_metadata.indexes.push_back(idb_index_metadata);
}
metadata.object_stores.push_back(idb_store_metadata);
}
return metadata;
}
void IndexedDBDispatcherHost::OnIDBFactoryGetDatabaseNames(
const IndexedDBHostMsg_FactoryGetDatabaseNames_Params& params) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
base::FilePath indexed_db_path = indexed_db_context_->data_path();
GURL origin_url =
storage::GetOriginFromIdentifier(params.database_identifier);
Context()->GetIDBFactory()->GetDatabaseNames(
new IndexedDBCallbacks(
this, params.ipc_thread_id, params.ipc_callbacks_id),
origin_url,
indexed_db_path,
request_context_);
}
void IndexedDBDispatcherHost::OnIDBFactoryOpen(
const IndexedDBHostMsg_FactoryOpen_Params& params) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
base::TimeTicks begin_time = base::TimeTicks::Now();
base::FilePath indexed_db_path = indexed_db_context_->data_path();
GURL origin_url =
storage::GetOriginFromIdentifier(params.database_identifier);
int64 host_transaction_id = HostTransactionId(params.transaction_id);
// TODO(dgrogan): Don't let a non-existing database be opened (and therefore
// created) if this origin is already over quota.
scoped_refptr<IndexedDBCallbacks> callbacks =
new IndexedDBCallbacks(this,
params.ipc_thread_id,
params.ipc_callbacks_id,
params.ipc_database_callbacks_id,
host_transaction_id,
origin_url);
callbacks->SetConnectionOpenStartTime(begin_time);
scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks =
new IndexedDBDatabaseCallbacks(
this, params.ipc_thread_id, params.ipc_database_callbacks_id);
IndexedDBPendingConnection connection(callbacks,
database_callbacks,
ipc_process_id_,
host_transaction_id,
params.version);
DCHECK(request_context_);
Context()->GetIDBFactory()->Open(
params.name, connection, request_context_, origin_url, indexed_db_path);
}
void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
const IndexedDBHostMsg_FactoryDeleteDatabase_Params& params) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
GURL origin_url =
storage::GetOriginFromIdentifier(params.database_identifier);
base::FilePath indexed_db_path = indexed_db_context_->data_path();
DCHECK(request_context_);
Context()->GetIDBFactory()->DeleteDatabase(
params.name,
request_context_,
new IndexedDBCallbacks(
this, params.ipc_thread_id, params.ipc_callbacks_id),
origin_url,
indexed_db_path);
}
// OnPutHelper exists only to allow us to hop threads while holding a reference
// to the IndexedDBDispatcherHost.
void IndexedDBDispatcherHost::OnPutHelper(
const IndexedDBHostMsg_DatabasePut_Params& params,
std::vector<storage::BlobDataHandle*> handles) {
database_dispatcher_host_->OnPut(params, handles);
}
void IndexedDBDispatcherHost::OnAckReceivedBlobs(
const std::vector<std::string>& uuids) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
for (const auto& uuid : uuids)
DropBlobData(uuid);
}
void IndexedDBDispatcherHost::FinishTransaction(int64 host_transaction_id,
bool committed) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
if (!database_dispatcher_host_)
return;
TransactionIDToURLMap& transaction_url_map =
database_dispatcher_host_->transaction_url_map_;
TransactionIDToSizeMap& transaction_size_map =
database_dispatcher_host_->transaction_size_map_;
TransactionIDToDatabaseIDMap& transaction_database_map =
database_dispatcher_host_->transaction_database_map_;
if (committed)
Context()->TransactionComplete(transaction_url_map[host_transaction_id]);
transaction_url_map.erase(host_transaction_id);
transaction_size_map.erase(host_transaction_id);
transaction_database_map.erase(host_transaction_id);
}
//////////////////////////////////////////////////////////////////////
// Helper templates.
//
template <typename ObjectType>
ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
IDMap<ObjectType, IDMapOwnPointer>* map,
int32 ipc_return_object_id) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
ObjectType* return_object = map->Lookup(ipc_return_object_id);
if (!return_object) {
NOTREACHED() << "Uh oh, couldn't find object with id "
<< ipc_return_object_id;
RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
BadMessageReceived();
}
return return_object;
}
template <typename ObjectType>
ObjectType* IndexedDBDispatcherHost::GetOrTerminateProcess(
RefIDMap<ObjectType>* map,
int32 ipc_return_object_id) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
ObjectType* return_object = map->Lookup(ipc_return_object_id);
if (!return_object) {
NOTREACHED() << "Uh oh, couldn't find object with id "
<< ipc_return_object_id;
RecordAction(base::UserMetricsAction("BadMessageTerminate_IDBMF"));
BadMessageReceived();
}
return return_object;
}
template <typename MapType>
void IndexedDBDispatcherHost::DestroyObject(MapType* map, int32 ipc_object_id) {
GetOrTerminateProcess(map, ipc_object_id);
map->Remove(ipc_object_id);
}
//////////////////////////////////////////////////////////////////////
// IndexedDBDispatcherHost::DatabaseDispatcherHost
//
IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost(
IndexedDBDispatcherHost* parent)
: parent_(parent) {
map_.set_check_on_null_data(true);
}
IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() {
// TODO(alecflett): uncomment these when we find the source of these leaks.
// DCHECK(transaction_size_map_.empty());
// DCHECK(transaction_url_map_.empty());
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::CloseAll() {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
// Abort outstanding transactions started by connections in the associated
// front-end to unblock later transactions. This should only occur on unclean
// (crash) or abrupt (process-kill) shutdowns.
for (TransactionIDToDatabaseIDMap::iterator iter =
transaction_database_map_.begin();
iter != transaction_database_map_.end();) {
int64 transaction_id = iter->first;
int32 ipc_database_id = iter->second;
++iter;
IndexedDBConnection* connection = map_.Lookup(ipc_database_id);
if (connection && connection->IsConnected()) {
connection->database()->Abort(
transaction_id,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError));
}
}
DCHECK(transaction_database_map_.empty());
for (const auto& iter : database_url_map_) {
IndexedDBConnection* connection = map_.Lookup(iter.first);
if (connection && connection->IsConnected()) {
connection->Close();
parent_->Context()->ConnectionClosed(iter.second, connection);
}
}
}
bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
const IPC::Message& message) {
DCHECK(
(message.type() == IndexedDBHostMsg_DatabasePut::ID ||
message.type() == IndexedDBHostMsg_AckReceivedBlobs::ID) ||
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(
IndexedDBDispatcherHost::DatabaseDispatcherHost, message)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateObjectStore,
OnCreateObjectStore)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteObjectStore,
OnDeleteObjectStore)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateTransaction,
OnCreateTransaction)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClose, OnClose)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseVersionChangeIgnored,
OnVersionChangeIgnored)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDestroyed, OnDestroyed)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseGet, OnGet)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabasePut, OnPutWrapper)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexKeys, OnSetIndexKeys)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseSetIndexesReady,
OnSetIndexesReady)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseOpenCursor, OnOpenCursor)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCount, OnCount)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteRange, OnDeleteRange)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseClear, OnClear)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCreateIndex, OnCreateIndex)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseDeleteIndex, OnDeleteIndex)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseAbort, OnAbort)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_DatabaseCommit, OnCommit)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore(
const IndexedDBHostMsg_DatabaseCreateObjectStore_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
connection->database()->CreateObjectStore(host_transaction_id,
params.object_store_id,
params.name,
params.key_path,
params.auto_increment);
if (parent_->Context()->IsOverQuota(
database_url_map_[params.ipc_database_id])) {
connection->database()->Abort(
host_transaction_id,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
}
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore(
int32 ipc_database_id,
int64 transaction_id,
int64 object_store_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
connection->database()->DeleteObjectStore(
parent_->HostTransactionId(transaction_id), object_store_id);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateTransaction(
const IndexedDBHostMsg_DatabaseCreateTransaction_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
if (transaction_database_map_.find(host_transaction_id) !=
transaction_database_map_.end()) {
DLOG(ERROR) << "Duplicate host_transaction_id.";
return;
}
connection->database()->CreateTransaction(
host_transaction_id, connection, params.object_store_ids, params.mode);
transaction_database_map_[host_transaction_id] = params.ipc_database_id;
parent_->RegisterTransactionId(host_transaction_id,
database_url_map_[params.ipc_database_id]);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose(
int32 ipc_database_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
connection->Close();
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnVersionChangeIgnored(
int32 ipc_database_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
connection->VersionChangeIgnored();
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
int32 ipc_object_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_object_id);
if (!connection)
return;
if (connection->IsConnected())
connection->Close();
parent_->Context()
->ConnectionClosed(database_url_map_[ipc_object_id], connection);
database_url_map_.erase(ipc_object_id);
parent_->DestroyObject(&map_, ipc_object_id);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnGet(
const IndexedDBHostMsg_DatabaseGet_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
parent_, params.ipc_thread_id, params.ipc_callbacks_id));
connection->database()->Get(
parent_->HostTransactionId(params.transaction_id),
params.object_store_id,
params.index_id,
make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
params.key_only,
callbacks);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPutWrapper(
const IndexedDBHostMsg_DatabasePut_Params& params) {
std::vector<storage::BlobDataHandle*> handles;
for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
handles.push_back(parent_->blob_storage_context_->context()
->GetBlobDataFromUUID(info.uuid)
.release());
}
parent_->indexed_db_context_->TaskRunner()->PostTask(
FROM_HERE,
base::Bind(
&IndexedDBDispatcherHost::OnPutHelper, parent_, params, handles));
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnPut(
const IndexedDBHostMsg_DatabasePut_Params& params,
std::vector<storage::BlobDataHandle*> handles) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
ScopedVector<storage::BlobDataHandle> scoped_handles;
scoped_handles.swap(handles);
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
parent_, params.ipc_thread_id, params.ipc_callbacks_id));
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
std::vector<IndexedDBBlobInfo> blob_info(params.blob_or_file_info.size());
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
for (size_t i = 0; i < params.blob_or_file_info.size(); ++i) {
const IndexedDBMsg_BlobOrFileInfo& info = params.blob_or_file_info[i];
if (info.is_file) {
base::FilePath path;
if (!info.file_path.empty()) {
path = base::FilePath::FromUTF16Unsafe(info.file_path);
if (!policy->CanReadFile(parent_->ipc_process_id_, path)) {
parent_->BadMessageReceived();
return;
}
}
blob_info[i] =
IndexedDBBlobInfo(info.uuid, path, info.file_name, info.mime_type);
if (info.size != static_cast<uint64_t>(-1)) {
blob_info[i].set_last_modified(
base::Time::FromDoubleT(info.last_modified));
blob_info[i].set_size(info.size);
}
} else {
blob_info[i] = IndexedDBBlobInfo(info.uuid, info.mime_type, info.size);
}
}
// TODO(alecflett): Avoid a copy here.
IndexedDBValue value;
value.bits = params.value;
value.blob_info.swap(blob_info);
connection->database()->Put(host_transaction_id,
params.object_store_id,
&value,
&scoped_handles,
make_scoped_ptr(new IndexedDBKey(params.key)),
params.put_mode,
callbacks,
params.index_keys);
TransactionIDToSizeMap* map =
&parent_->database_dispatcher_host_->transaction_size_map_;
// Size can't be big enough to overflow because it represents the
// actual bytes passed through IPC.
(*map)[host_transaction_id] += params.value.size();
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexKeys(
const IndexedDBHostMsg_DatabaseSetIndexKeys_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
connection->database()->SetIndexKeys(
host_transaction_id,
params.object_store_id,
make_scoped_ptr(new IndexedDBKey(params.primary_key)),
params.index_keys);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetIndexesReady(
int32 ipc_database_id,
int64 transaction_id,
int64 object_store_id,
const std::vector<int64>& index_ids) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
connection->database()->SetIndexesReady(
parent_->HostTransactionId(transaction_id), object_store_id, index_ids);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpenCursor(
const IndexedDBHostMsg_DatabaseOpenCursor_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
parent_, params.ipc_thread_id, params.ipc_callbacks_id, -1));
connection->database()->OpenCursor(
parent_->HostTransactionId(params.transaction_id),
params.object_store_id,
params.index_id,
make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
params.direction,
params.key_only,
params.task_type,
callbacks);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCount(
const IndexedDBHostMsg_DatabaseCount_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
parent_, params.ipc_thread_id, params.ipc_callbacks_id));
connection->database()->Count(
parent_->HostTransactionId(params.transaction_id),
params.object_store_id,
params.index_id,
make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
callbacks);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteRange(
const IndexedDBHostMsg_DatabaseDeleteRange_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
parent_, params.ipc_thread_id, params.ipc_callbacks_id));
connection->database()->DeleteRange(
parent_->HostTransactionId(params.transaction_id),
params.object_store_id,
make_scoped_ptr(new IndexedDBKeyRange(params.key_range)),
callbacks);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClear(
int32 ipc_thread_id,
int32 ipc_callbacks_id,
int32 ipc_database_id,
int64 transaction_id,
int64 object_store_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
scoped_refptr<IndexedDBCallbacks> callbacks(
new IndexedDBCallbacks(parent_, ipc_thread_id, ipc_callbacks_id));
connection->database()->Clear(
parent_->HostTransactionId(transaction_id), object_store_id, callbacks);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnAbort(
int32 ipc_database_id,
int64 transaction_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
connection->database()->Abort(parent_->HostTransactionId(transaction_id));
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCommit(
int32 ipc_database_id,
int64 transaction_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
int64 host_transaction_id = parent_->HostTransactionId(transaction_id);
int64 transaction_size = transaction_size_map_[host_transaction_id];
if (transaction_size &&
parent_->Context()->WouldBeOverQuota(
transaction_url_map_[host_transaction_id], transaction_size)) {
connection->database()->Abort(
host_transaction_id,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
return;
}
connection->database()->Commit(host_transaction_id);
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateIndex(
const IndexedDBHostMsg_DatabaseCreateIndex_Params& params) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, params.ipc_database_id);
if (!connection || !connection->IsConnected())
return;
int64 host_transaction_id = parent_->HostTransactionId(params.transaction_id);
connection->database()->CreateIndex(host_transaction_id,
params.object_store_id,
params.index_id,
params.name,
params.key_path,
params.unique,
params.multi_entry);
if (parent_->Context()->IsOverQuota(
database_url_map_[params.ipc_database_id])) {
connection->database()->Abort(
host_transaction_id,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError));
}
}
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteIndex(
int32 ipc_database_id,
int64 transaction_id,
int64 object_store_id,
int64 index_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBConnection* connection =
parent_->GetOrTerminateProcess(&map_, ipc_database_id);
if (!connection || !connection->IsConnected())
return;
connection->database()->DeleteIndex(
parent_->HostTransactionId(transaction_id), object_store_id, index_id);
}
//////////////////////////////////////////////////////////////////////
// IndexedDBDispatcherHost::CursorDispatcherHost
//
IndexedDBDispatcherHost::CursorDispatcherHost::CursorDispatcherHost(
IndexedDBDispatcherHost* parent)
: parent_(parent) {
map_.set_check_on_null_data(true);
}
IndexedDBDispatcherHost::CursorDispatcherHost::~CursorDispatcherHost() {}
bool IndexedDBDispatcherHost::CursorDispatcherHost::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(
IndexedDBDispatcherHost::CursorDispatcherHost, message)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorAdvance, OnAdvance)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorContinue, OnContinue)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetch, OnPrefetch)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorPrefetchReset, OnPrefetchReset)
IPC_MESSAGE_HANDLER(IndexedDBHostMsg_CursorDestroyed, OnDestroyed)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
DCHECK(
!handled ||
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
return handled;
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnAdvance(
int32 ipc_cursor_id,
int32 ipc_thread_id,
int32 ipc_callbacks_id,
uint32 count) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBCursor* idb_cursor =
parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
idb_cursor->Advance(
count,
new IndexedDBCallbacks(
parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnContinue(
int32 ipc_cursor_id,
int32 ipc_thread_id,
int32 ipc_callbacks_id,
const IndexedDBKey& key,
const IndexedDBKey& primary_key) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBCursor* idb_cursor =
parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
idb_cursor->Continue(
key.IsValid() ? make_scoped_ptr(new IndexedDBKey(key))
: scoped_ptr<IndexedDBKey>(),
primary_key.IsValid() ? make_scoped_ptr(new IndexedDBKey(primary_key))
: scoped_ptr<IndexedDBKey>(),
new IndexedDBCallbacks(
parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetch(
int32 ipc_cursor_id,
int32 ipc_thread_id,
int32 ipc_callbacks_id,
int n) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBCursor* idb_cursor =
parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
idb_cursor->PrefetchContinue(
n,
new IndexedDBCallbacks(
parent_, ipc_thread_id, ipc_callbacks_id, ipc_cursor_id));
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnPrefetchReset(
int32 ipc_cursor_id,
int used_prefetches,
int unused_prefetches) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
IndexedDBCursor* idb_cursor =
parent_->GetOrTerminateProcess(&map_, ipc_cursor_id);
if (!idb_cursor)
return;
leveldb::Status s =
idb_cursor->PrefetchReset(used_prefetches, unused_prefetches);
// TODO(cmumford): Handle this error (crbug.com/363397)
if (!s.ok())
DLOG(ERROR) << "Unable to reset prefetch";
}
void IndexedDBDispatcherHost::CursorDispatcherHost::OnDestroyed(
int32 ipc_object_id) {
DCHECK(
parent_->indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
parent_->DestroyObject(&map_, ipc_object_id);
}
} // namespace content