Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Unified Diff: content/browser/indexed_db/indexed_db_backing_store.cc

Issue 240003011: Add blob-writing functionality [as yet un-called] to IDB's backend. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Factor out GetBlobFilenameForKey Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/indexed_db/indexed_db_backing_store.cc
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index d79407aec9d809ca91a8faf2b8da9ee8c1ee63e5..402afa670a31bc4ec453f3a2c4f924f1a289ec65 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/indexed_db/indexed_db_blob_info.h"
@@ -26,18 +27,54 @@
#include "content/common/indexed_db/indexed_db_key.h"
#include "content/common/indexed_db/indexed_db_key_path.h"
#include "content/common/indexed_db/indexed_db_key_range.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/url_request/url_request_context.h"
#include "third_party/WebKit/public/platform/WebIDBTypes.h"
#include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "webkit/browser/blob/blob_data_handle.h"
+#include "webkit/browser/fileapi/file_stream_writer.h"
+#include "webkit/browser/fileapi/file_writer_delegate.h"
+#include "webkit/browser/fileapi/local_file_stream_writer.h"
#include "webkit/common/database/database_identifier.h"
+using base::FilePath;
using base::StringPiece;
+using fileapi::FileWriterDelegate;
namespace content {
namespace {
+FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) {
+ return pathBase.AppendASCII(base::StringPrintf("%lx", database_id));
+}
+
+FilePath GetBlobDirectoryNameForKey(const FilePath& pathBase,
+ int64 database_id,
+ int64 key) {
+ FilePath path = GetBlobDirectoryName(pathBase, database_id);
+ path = path.AppendASCII(base::StringPrintf(
+ "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
+ return path;
+}
+
+FilePath GetBlobFileNameForKey(const FilePath& pathBase,
+ int64 database_id,
+ int64 key) {
+ FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
+ path = path.AppendASCII(base::StringPrintf("%lx", key));
+ return path;
+}
+
+// This assumes a file path of dbId/second-to-LSB-of-counter/counter.
+bool MakeIDBBlobDirectory(const FilePath& pathBase,
+ int64 database_id,
+ int64 key) {
+ FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
+ return base::CreateDirectory(path);
+}
+
static std::string ComputeOriginIdentifier(const GURL& origin_url) {
return webkit_database::GetIdentifierFromOrigin(origin_url) + "@1";
}
@@ -441,6 +478,7 @@ IndexedDBBackingStore::IndexedDBBackingStore(
IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& blob_path,
+ net::URLRequestContext* request_context,
scoped_ptr<LevelDBDatabase> db,
scoped_ptr<LevelDBComparator> comparator,
base::TaskRunner* task_runner)
@@ -448,6 +486,7 @@ IndexedDBBackingStore::IndexedDBBackingStore(
origin_url_(origin_url),
blob_path_(blob_path),
origin_identifier_(ComputeOriginIdentifier(origin_url)),
+ request_context_(request_context),
task_runner_(task_runner),
db_(db.Pass()),
comparator_(comparator.Pass()),
@@ -506,6 +545,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* disk_full,
@@ -515,6 +555,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
return IndexedDBBackingStore::Open(indexed_db_factory,
origin_url,
path_base,
+ request_context,
data_loss,
data_loss_message,
disk_full,
@@ -655,6 +696,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
+ net::URLRequestContext* request_context,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* is_disk_full,
@@ -780,6 +822,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
return Create(indexed_db_factory,
origin_url,
blob_path,
+ request_context,
db.Pass(),
comparator.Pass(),
task_runner);
@@ -815,6 +858,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
return Create(NULL /* indexed_db_factory */,
origin_url,
base::FilePath(),
+ NULL /* request_context */,
db.Pass(),
comparator.Pass(),
task_runner);
@@ -825,15 +869,16 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& blob_path,
+ net::URLRequestContext* request_context,
scoped_ptr<LevelDBDatabase> db,
scoped_ptr<LevelDBComparator> comparator,
base::TaskRunner* task_runner) {
// TODO(jsbell): Handle comparator name changes.
-
scoped_refptr<IndexedDBBackingStore> backing_store(
new IndexedDBBackingStore(indexed_db_factory,
origin_url,
blob_path,
+ request_context,
db.Pass(),
comparator.Pass(),
task_runner));
@@ -1693,6 +1738,197 @@ leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
return s;
}
+class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
+ : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
+ public:
+ typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
+ WriteDescriptorVec;
+ ChainedBlobWriterImpl(
+ int64 database_id,
+ IndexedDBBackingStore* backingStore,
+ WriteDescriptorVec& blobs,
+ scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
+ : waiting_for_callback_(false),
+ database_id_(database_id),
+ backing_store_(backingStore),
+ callback_(callback),
+ aborted_(false) {
+ blobs_.swap(blobs);
+ iter_ = blobs_.begin();
+ WriteNextFile();
+ }
+
+ void set_delegate(scoped_ptr<FileWriterDelegate> delegate) {
+ delegate_.reset(delegate.release());
+ }
+
+ void ReportWriteCompletion(bool succeeded, int64 bytes_written) {
+ // TODO(ericu): Check bytes_written against the blob's snapshot value.
+ DCHECK(waiting_for_callback_);
+ DCHECK(!succeeded || bytes_written >= 0);
+ waiting_for_callback_ = false;
+ if (delegate_.get()) // Only present for Blob, not File.
+ content::BrowserThread::DeleteSoon(
+ content::BrowserThread::IO, FROM_HERE, delegate_.release());
+ if (aborted_) {
+ self_ref_ = NULL;
+ return;
+ }
+ if (succeeded)
+ WriteNextFile();
+ else
+ callback_->Run(false);
+ }
+
+ void Abort() {
+ if (!waiting_for_callback_)
+ return;
+ self_ref_ = this;
+ aborted_ = true;
+ }
+
+ private:
+ void WriteNextFile() {
+ DCHECK(!waiting_for_callback_);
+ DCHECK(!aborted_);
+ if (iter_ == blobs_.end()) {
+ DCHECK(!self_ref_);
+ callback_->Run(true);
+ return;
+ } else {
+ if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
+ callback_->Run(false);
+ return;
+ }
+ waiting_for_callback_ = true;
+ ++iter_;
+ }
+ }
+
+ bool waiting_for_callback_;
+ scoped_refptr<ChainedBlobWriterImpl> self_ref_;
+ WriteDescriptorVec blobs_;
+ WriteDescriptorVec::const_iterator iter_;
+ int64 database_id_;
+ IndexedDBBackingStore* backing_store_;
+ scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
+ scoped_ptr<FileWriterDelegate> delegate_;
+ bool aborted_;
+};
+
+class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
+ public base::RefCounted<LocalWriteClosure> {
+ public:
+ LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
+ chained_blob_writer_,
+ base::TaskRunner* task_runner)
+ : chained_blob_writer_(chained_blob_writer_),
+ task_runner_(task_runner),
+ bytes_written_(-1) {}
+
+ void Run(base::File::Error rv,
+ int64 bytes,
+ FileWriterDelegate::WriteProgressStatus write_status) {
+ if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
+ return; // We don't care about progress events.
+ if (rv == base::File::FILE_OK) {
+ DCHECK(bytes >= 0);
+ DCHECK(write_status == FileWriterDelegate::SUCCESS_COMPLETED);
+ bytes_written_ = bytes;
+ } else {
+ DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
+ write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
+ }
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&LocalWriteClosure::callBlobCallbackOnIDBTaskRunner,
+ this,
+ write_status == FileWriterDelegate::SUCCESS_COMPLETED));
+ }
+
+ void writeBlobToFileOnIOThread(const FilePath& file_path,
+ const GURL& blob_url,
+ net::URLRequestContext* request_context) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ scoped_ptr<fileapi::FileStreamWriter> writer(
+ fileapi::FileStreamWriter::CreateForLocalFile(
+ task_runner_, file_path, 0,
+ fileapi::FileStreamWriter::CREATE_NEW_FILE));
+ scoped_ptr<FileWriterDelegate> delegate(
+ new FileWriterDelegate(writer.Pass()));
+
+ DCHECK(blob_url.is_valid());
+ scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
+ blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
+
+ delegate->Start(blob_request.Pass(),
+ base::Bind(&LocalWriteClosure::Run, this));
+ chained_blob_writer_->set_delegate(delegate.Pass());
+ }
+
+ private:
+ void callBlobCallbackOnIDBTaskRunner(bool succeeded) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ chained_blob_writer_->ReportWriteCompletion(succeeded, bytes_written_);
+ }
+
+ IndexedDBBackingStore::Transaction::ChainedBlobWriter* chained_blob_writer_;
+ base::TaskRunner* task_runner_;
+ int64 bytes_written_;
+};
+
+bool IndexedDBBackingStore::WriteBlobFile(
+ int64 database_id,
+ const Transaction::WriteDescriptor& descriptor,
+ Transaction::ChainedBlobWriter* chained_blob_writer) {
+
+ if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
+ return false;
+
+ FilePath path = GetBlobFileName(database_id, descriptor.key());
+
+ if (descriptor.is_file()) {
+ DCHECK(!descriptor.file_path().empty());
+ if (!base::CopyFile(descriptor.file_path(), path))
+ return false;
+
+ base::File::Info info;
+ if (base::GetFileInfo(descriptor.file_path(), &info)) {
+ // TODO(ericu): Validate the snapshot date here. Expand WriteDescriptor
+ // to include snapshot date and file size, and check both.
+ if (!base::TouchFile(path, info.last_accessed, info.last_modified))
+ ; // TODO(ericu): Complain quietly; timestamp's probably not vital.
+ } else {
+ ; // TODO(ericu): Complain quietly; timestamp's probably not vital.
+ }
+
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
+ chained_blob_writer,
+ true,
+ info.size));
+ } else {
+ DCHECK(descriptor.url().is_valid());
+ scoped_refptr<LocalWriteClosure> write_closure(
+ new LocalWriteClosure(chained_blob_writer, task_runner_));
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
+ write_closure.get(),
+ path,
+ descriptor.url(),
+ request_context_));
+ }
+ return true;
+}
+
+// This assumes a file path of dbId/second-to-LSB-of-counter/counter.
jsbell 2014/04/28 18:04:20 Nit: This comment could be moved/removed now.
+FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
+ return GetBlobFileNameForKey(blob_path_, database_id, key);
+}
+
static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
const std::string& stop_key,
int64 index_id,
@@ -1809,6 +2045,16 @@ leveldb::Status IndexedDBBackingStore::GetIndexes(
return s;
}
+bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
+ FilePath fileName = GetBlobFileName(database_id, key);
+ return base::DeleteFile(fileName, false);
+}
+
+bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
+ FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
+ return base::DeleteFile(dirName, true);
+}
+
WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
LevelDBTransaction* transaction,
int64 database_id,
@@ -2946,9 +3192,56 @@ leveldb::Status IndexedDBBackingStore::Transaction::Commit() {
return s;
}
+
+class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
+ : public IndexedDBBackingStore::BlobWriteCallback {
+ public:
+ BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
+ scoped_refptr<BlobWriteCallback> callback)
+ : transaction_(transaction), callback_(callback) {}
+ virtual void Run(bool succeeded) {
+ callback_->Run(succeeded);
+ transaction_->chained_blob_writer_ = NULL;
+ }
+
+ private:
+ IndexedDBBackingStore::Transaction* transaction_;
+ scoped_refptr<BlobWriteCallback> callback_;
+};
+
+void IndexedDBBackingStore::Transaction::WriteNewBlobs(
+ BlobEntryKeyValuePairVec& new_blob_entries,
+ WriteDescriptorVec& new_files_to_write,
+ scoped_refptr<BlobWriteCallback> callback) {
+ DCHECK_GT(new_files_to_write.size(), 0UL);
+ DCHECK_GT(database_id_, 0);
+ BlobEntryKeyValuePairVec::iterator blob_entry_iter;
+ for (blob_entry_iter = new_blob_entries.begin();
+ blob_entry_iter != new_blob_entries.end();
+ ++blob_entry_iter) {
+ // Add the new blob-table entry for each blob to the main transaction, or
+ // remove any entry that may exist if there's no new one.
+ if (!blob_entry_iter->second.size())
+ transaction_->Remove(blob_entry_iter->first.Encode());
+ else
+ transaction_->Put(blob_entry_iter->first.Encode(),
+ &blob_entry_iter->second);
+ }
+ // Creating the writer will start it going asynchronously.
+ chained_blob_writer_ =
+ new ChainedBlobWriterImpl(database_id_,
+ backing_store_,
+ new_files_to_write,
+ new BlobWriteCallbackWrapper(this, callback));
+}
+
void IndexedDBBackingStore::Transaction::Rollback() {
IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
DCHECK(transaction_.get());
+ if (chained_blob_writer_) {
+ chained_blob_writer_->Abort();
+ chained_blob_writer_ = NULL;
+ }
transaction_->Rollback();
transaction_ = NULL;
}
@@ -3004,4 +3297,14 @@ void IndexedDBBackingStore::Transaction::PutBlobInfo(
DCHECK(!handles || !handles->size());
}
+IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
+ const GURL& url,
+ int64_t key)
+ : is_file_(false), url_(url), key_(key) {}
+
+IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
+ const FilePath& file_path,
+ int64_t key)
+ : is_file_(true), file_path_(file_path), key_(key) {}
+
} // namespace content

Powered by Google App Engine
This is Rietveld 408576698