| 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;
|
| }
|
|
|
|
|