// 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 #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) {} 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) {} 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) {} 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& 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 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_); 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 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 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 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 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 blob_data_handle( blob_storage_context->GetBlobDataFromUUID(uuid)); dispatcher_host->HoldBlobDataHandle(uuid, blob_data_handle); return uuid; } scoped_refptr 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 blob_data = new webkit_blob::BlobData(uuid); blob_data->AppendFile( blob_info.file_path(), 0, blob_info.size(), blob_info.last_modified()); scoped_ptr blob_data_handle( blob_storage_context->AddFinishedBlob(blob_data.get())); dispatcher_host->HoldBlobDataHandle(uuid, blob_data_handle); return uuid; } static bool CreateAllBlobs( const std::vector& blob_info, std::vector* blob_or_file_info, scoped_refptr 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 static void CreateBlobsAndSend( ParamType* params, scoped_refptr dispatcher_host, const std::vector& blob_info, std::vector* 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 dispatcher_host, const std::vector& values, std::vector >* blob_or_file_infos) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_EQ(values.size(), blob_or_file_infos->size()); std::vector::const_iterator value_iter; std::vector >::iterator blob_iter; for (value_iter = values.begin(), blob_iter = 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& blob_info, std::vector* blob_or_file_info) { for (std::vector::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& blob_info, const base::Closure& callback) { std::vector::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 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 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, 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 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& keys, const std::vector& primary_keys, std::vector& 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 msgKeys; std::vector msgPrimaryKeys; for (size_t i = 0; i < keys.size(); ++i) { msgKeys.push_back(keys[i]); msgPrimaryKeys.push_back(primary_keys[i]); } scoped_ptr 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& values_bits = params->values; values_bits.resize(values.size()); std::vector >& values_blob_infos = params->blob_or_file_infos; values_blob_infos.resize(values.size()); bool found_blob_info = false; std::vector::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::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, base::Unretained(¶ms->blob_or_file_infos))); } 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 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, 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 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, 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