Index: content/browser/indexed_db/indexed_db_callbacks.cc |
diff --git a/content/browser/indexed_db/indexed_db_callbacks.cc b/content/browser/indexed_db/indexed_db_callbacks.cc |
index 46270db75311620877979a1bec17e1464fc9b51e..3a16ee35b838a1ca4906108ec12f5dee8aa44b3d 100644 |
--- a/content/browser/indexed_db/indexed_db_callbacks.cc |
+++ b/content/browser/indexed_db/indexed_db_callbacks.cc |
@@ -6,15 +6,25 @@ |
#include <algorithm> |
+#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_messages.h" |
#include "webkit/browser/quota/quota_manager.h" |
+#include "webkit/common/blob/blob_data.h" |
+#include "webkit/common/blob/shareable_file_reference.cc" |
using WebKit::WebIDBCallbacks; |
+using webkit_blob::ShareableFileReference; |
namespace content { |
@@ -158,10 +168,177 @@ void IndexedDBCallbacks::OnSuccess(scoped_ptr<IndexedDBConnection> connection, |
dispatcher_host_ = NULL; |
} |
+// Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx |
+// TODO: Remove this. |
+static std::string MakeFakeGuid() { |
+ static int32 counter = 0; |
+ return base::StringPrintf("00000000-0000-4000-8000-0000%08x", counter++); |
+} |
+ |
+// TODO: Remove this when Michael gives us a better way to do it. |
+static GURL CreateBlob( |
+ const IndexedDBBlobInfo& blob_info, |
+ FileAPIMessageFilter* file_api_message_filter, |
+ base::TaskRunner* task_runner) { |
+ 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); |
+ shareable_file->AddFinalReleaseCallback(blob_info.release_callback()); |
+ } |
+ |
+ std::string blob_uuid(MakeFakeGuid()); |
+ GURL blob_url("blob:blobinternal%a///" + MakeFakeGuid()); |
+ webkit_blob::BlobData::Item item; |
+ item.SetToFilePath(blob_info.file_path()); |
+ |
+ // It would be much nicer not to go through the FileAPIMessageFilter here, but |
+ // if we go around it, it doesn't know about the blob we create, and can't |
+ // free it later when the renderer exits. |
+ file_api_message_filter->OnStartBuildingBlob(blob_uuid); |
+ file_api_message_filter->OnAppendBlobDataItemToBlob(blob_uuid, item); |
+ file_api_message_filter->OnFinishBuildingBlob(blob_uuid, |
+ base::UTF16ToUTF8(blob_info.type())); |
+ file_api_message_filter->OnDeprecatedRegisterBlobURL(blob_url, blob_uuid); |
+ // We've just added an extra refcount for blob_uuid that will never be cleaned |
+ // up. We should remove it as soon as the blob gets cloned by the renderer, |
+ // but we have no way of knowing when that's happened. So for now I put in |
+ // this huge HACK to do a delayed decrement after what should be a safe delay |
+ // in most cases. |
+ if (!BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE, |
+ base::Bind(&FileAPIMessageFilter::OnDeprecatedRevokeBlobURL, |
+ file_api_message_filter, blob_url), base::TimeDelta::FromSeconds(10))) { |
+ fprintf(stderr, "ERICU: We're going to leak a blob.\n"); |
+ } |
+ return blob_url; |
+} |
+ |
+static void CreateAllBlobs( |
+ const std::vector<IndexedDBBlobInfo>& blob_info, |
+ std::vector<IndexedDBMsg_BlobOrFileInfo>* blob_or_file_info, |
+ FileAPIMessageFilter* file_api_message_filter, |
+ base::TaskRunner* task_runner) { |
+ size_t i; |
+ for (i = 0; i < blob_info.size(); ++i) { |
+ (*blob_or_file_info)[i].url = |
+ CreateBlob(blob_info[i], file_api_message_filter, task_runner); |
+ } |
+} |
+ |
+static void BlobLookupForIDBCursor( |
+ IndexedDBMsg_CallbacksSuccessIDBCursor_Params* 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)); |
+ CreateAllBlobs(blob_info, blob_or_file_info, |
+ dispatcher_host->file_api_message_filter(), |
+ dispatcher_host->Context()->TaskRunner()); |
+ dispatcher_host->Send( |
+ new IndexedDBMsg_CallbacksSuccessIDBCursor(*params)); |
+} |
+ |
+static void BlobLookupForCursorContinue( |
+ IndexedDBMsg_CallbacksSuccessCursorContinue_Params* 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)); |
+ CreateAllBlobs(blob_info, blob_or_file_info, |
+ dispatcher_host->file_api_message_filter(), |
+ dispatcher_host->Context()->TaskRunner()); |
+ dispatcher_host->Send( |
+ new IndexedDBMsg_CallbacksSuccessCursorContinue(*params)); |
+} |
+ |
+static void BlobLookupForValueWithKey( |
+ IndexedDBMsg_CallbacksSuccessValueWithKey_Params* 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)); |
+ CreateAllBlobs(blob_info, blob_or_file_info, |
+ dispatcher_host->file_api_message_filter(), |
+ dispatcher_host->Context()->TaskRunner()); |
+ dispatcher_host->Send( |
+ new IndexedDBMsg_CallbacksSuccessValueWithKey(*params)); |
+} |
+ |
+static void BlobLookupForValue( |
+ IndexedDBMsg_CallbacksSuccessValue_Params* 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)); |
+ CreateAllBlobs(blob_info, blob_or_file_info, |
+ dispatcher_host->file_api_message_filter(), |
+ dispatcher_host->Context()->TaskRunner()); |
+ dispatcher_host->Send(new IndexedDBMsg_CallbacksSuccessValue(*params)); |
+} |
+ |
+static void BlobLookupForCursorPrefetch( |
+ IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params* params, |
+ scoped_refptr<IndexedDBDispatcherHost> dispatcher_host, |
+ const std::vector<IndexedDBValue>& values, |
+ std::vector<std::vector<IndexedDBMsg_BlobOrFileInfo> >* |
+ blob_or_file_infos) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ DCHECK(values.size() == 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 = blob_or_file_infos->begin(); |
+ value_iter != values.end(); ++value_iter, ++blob_iter) { |
+ CreateAllBlobs(value_iter->blob_info, &*blob_iter, |
+ dispatcher_host->file_api_message_filter(), |
+ dispatcher_host->Context()->TaskRunner()); |
+ } |
+ 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(); |
+ DCHECK_NE(-1, iter->size()); |
+ 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) { |
+ 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, |
- std::string* value) { |
+ IndexedDBValue* value) { |
DCHECK(dispatcher_host_.get()); |
DCHECK_EQ(kNoCursor, ipc_cursor_id_); |
@@ -170,24 +347,34 @@ void IndexedDBCallbacks::OnSuccess(scoped_refptr<IndexedDBCursor> cursor, |
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_); |
int32 ipc_object_id = dispatcher_host_->Add(cursor.get()); |
- IndexedDBMsg_CallbacksSuccessIDBCursor_Params 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; |
+ 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); |
+ std::swap(params->value, value->bits); |
// TODO(alecflett): Avoid a copy here: the whole params object is |
// being copied into the message. |
- dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessIDBCursor(params)); |
- |
+ if (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(BlobLookupForIDBCursor, 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, |
- std::string* value) { |
+ IndexedDBValue* value) { |
DCHECK(dispatcher_host_.get()); |
DCHECK_NE(kNoCursor, ipc_cursor_id_); |
@@ -201,25 +388,37 @@ void IndexedDBCallbacks::OnSuccess(const IndexedDBKey& key, |
DCHECK(idb_cursor); |
if (!idb_cursor) |
return; |
- IndexedDBMsg_CallbacksSuccessCursorContinue_Params 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; |
+ |
+ 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); |
+ std::swap(params->value, value->bits); |
// TODO(alecflett): Avoid a copy here: the whole params object is |
// being copied into the message. |
- dispatcher_host_->Send( |
- new IndexedDBMsg_CallbacksSuccessCursorContinue(params)); |
+ if (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(BlobLookupForCursorContinue, 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, |
- const std::vector<std::string>& values) { |
+ std::vector<IndexedDBValue>& values) { |
DCHECK_EQ(keys.size(), primary_keys.size()); |
DCHECK_EQ(keys.size(), values.size()); |
@@ -238,19 +437,47 @@ void IndexedDBCallbacks::OnSuccessWithPrefetch( |
msgPrimaryKeys.push_back(primary_keys[i]); |
} |
- IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params 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; |
- params.values = values; |
- dispatcher_host_->Send( |
- new IndexedDBMsg_CallbacksSuccessCursorPrefetch(params)); |
+ 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(values.size()); |
+ std::vector<std::vector<IndexedDBMsg_BlobOrFileInfo> > |
+ values_blob_infos(values.size()); |
+ std::vector<IndexedDBValue>::iterator iter = values.begin(); |
+ |
+ bool found_blob_info = false; |
+ 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) { |
+ blob_iter->mark_used_callback(); |
+ } |
+ } |
+ } |
+ |
+ params->values.swap(values_bits); |
+ if (found_blob_info) { |
+ params->blob_or_file_infos.swap(values_blob_infos); |
+ 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(std::string* value, |
+void IndexedDBCallbacks::OnSuccess(IndexedDBValue* value, |
const IndexedDBKey& key, |
const IndexedDBKeyPath& key_path) { |
DCHECK(dispatcher_host_.get()); |
@@ -260,37 +487,54 @@ void IndexedDBCallbacks::OnSuccess(std::string* value, |
DCHECK_EQ(kNoDatabase, ipc_database_id_); |
DCHECK_EQ(kNoDatabaseCallbacks, ipc_database_callbacks_id_); |
- std::string value_copy; |
+ 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(value_copy, *value); |
- |
- dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessValueWithKey( |
- ipc_thread_id_, |
- ipc_callbacks_id_, |
- // TODO(alecflett): Avoid a copy here. |
- value_copy, |
- key, |
- key_path)); |
+ std::swap(params->value, value->bits); |
+ if (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(BlobLookupForValueWithKey, base::Owned(params.release()), |
+ dispatcher_host_, value->blob_info, |
+ base::Unretained(&p->blob_or_file_info))); |
+ } |
dispatcher_host_ = NULL; |
} |
-void IndexedDBCallbacks::OnSuccess(std::string* value) { |
+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_); |
- std::string value_copy; |
+ 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(value_copy, *value); |
- |
- dispatcher_host_->Send(new IndexedDBMsg_CallbacksSuccessValue( |
- ipc_thread_id_, |
- ipc_callbacks_id_, |
- // TODO(alecflett): avoid a copy here. |
- value_copy)); |
+ std::swap(params->value, value->bits); |
+ if (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(BlobLookupForValue, base::Owned(params.release()), |
+ dispatcher_host_, value->blob_info, |
+ base::Unretained(&p->blob_or_file_info))); |
+ } |
dispatcher_host_ = NULL; |
} |