diff options
author | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 04:50:16 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 04:50:16 +0000 |
commit | 16353271e5229a678ea6c4faa2e8f7b06f5599a6 (patch) | |
tree | df4cb2c5eff84976d29c305581213e9b3bda49ff /webkit | |
parent | a70fcc778be6675b740f6a921a9d69bdda7f9a1c (diff) | |
download | chromium_src-16353271e5229a678ea6c4faa2e8f7b06f5599a6.zip chromium_src-16353271e5229a678ea6c4faa2e8f7b06f5599a6.tar.gz chromium_src-16353271e5229a678ea6c4faa2e8f7b06f5599a6.tar.bz2 |
webkit: Add UploadFileSystemFileElementReader
BUG=141834
TEST=content_unittests --gtest_filter="UploadFileSystemFileElementReaderTest.*"
TBR=jam@chromium.org for content/content_tests.gypi
Review URL: https://codereview.chromium.org/11818017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176264 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
4 files changed, 463 insertions, 0 deletions
diff --git a/webkit/fileapi/upload_file_system_file_element_reader.cc b/webkit/fileapi/upload_file_system_file_element_reader.cc new file mode 100644 index 0000000..212640a --- /dev/null +++ b/webkit/fileapi/upload_file_system_file_element_reader.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2013 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 "webkit/fileapi/upload_file_system_file_element_reader.h" + +#include "base/bind.h" +#include "net/base/net_errors.h" +#include "webkit/blob/file_stream_reader.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_url.h" + +namespace fileapi { + +UploadFileSystemFileElementReader::UploadFileSystemFileElementReader( + FileSystemContext* file_system_context, + const GURL& url, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time) + : file_system_context_(file_system_context), + url_(url), + range_offset_(range_offset), + range_length_(range_length), + expected_modification_time_(expected_modification_time), + stream_length_(0), + position_(0), + weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +UploadFileSystemFileElementReader::~UploadFileSystemFileElementReader() { +} + +int UploadFileSystemFileElementReader::Init( + const net::CompletionCallback& callback) { + // Reset states. + weak_ptr_factory_.InvalidateWeakPtrs(); + stream_length_ = 0; + position_ = 0; + + // Initialize the stream reader and the length. + stream_reader_.reset( + file_system_context_->CreateFileStreamReader( + FileSystemURL(url_), range_offset_, expected_modification_time_)); + DCHECK(stream_reader_); + + const int result = stream_reader_->GetLength( + base::Bind(&UploadFileSystemFileElementReader::OnGetLength, + weak_ptr_factory_.GetWeakPtr(), + callback)); + if (result >= 0) { + stream_length_ = result; + return net::OK; + } + return result; +} + +uint64 UploadFileSystemFileElementReader::GetContentLength() const { + return std::min(stream_length_, range_length_); +} + +uint64 UploadFileSystemFileElementReader::BytesRemaining() const { + return GetContentLength() - position_; +} + +int UploadFileSystemFileElementReader::Read( + net::IOBuffer* buf, + int buf_length, + const net::CompletionCallback& callback) { + DCHECK_LT(0, buf_length); + DCHECK(stream_reader_); + + const uint64 num_bytes_to_read = + std::min(BytesRemaining(), static_cast<uint64>(buf_length)); + + if (num_bytes_to_read == 0) + return 0; + + const int result = stream_reader_->Read( + buf, num_bytes_to_read, + base::Bind(&UploadFileSystemFileElementReader::OnRead, + weak_ptr_factory_.GetWeakPtr(), + callback)); + if (result >= 0) + OnRead(net::CompletionCallback(), result); + return result; +} + +void UploadFileSystemFileElementReader::OnGetLength( + const net::CompletionCallback& callback, + int64 result) { + if (result >= 0) { + stream_length_ = result; + callback.Run(net::OK); + return; + } + callback.Run(result); +} + +void UploadFileSystemFileElementReader::OnRead( + const net::CompletionCallback& callback, + int result) { + if (result > 0) { + position_ += result; + DCHECK_LE(position_, GetContentLength()); + } + if (!callback.is_null()) + callback.Run(result); +} + +} // namespace fileapi diff --git a/webkit/fileapi/upload_file_system_file_element_reader.h b/webkit/fileapi/upload_file_system_file_element_reader.h new file mode 100644 index 0000000..19b3db1 --- /dev/null +++ b/webkit/fileapi/upload_file_system_file_element_reader.h @@ -0,0 +1,64 @@ +// Copyright (c) 2013 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. + +#ifndef WEBKIT_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_ +#define WEBKIT_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_ + +#include "base/memory/weak_ptr.h" +#include "base/time.h" +#include "googleurl/src/gurl.h" +#include "net/base/upload_element_reader.h" +#include "webkit/storage/webkit_storage_export.h" + +namespace webkit_blob { +class FileStreamReader; +} + +namespace fileapi { + +class FileSystemContext; + +// An UploadElementReader implementation for filesystem file. +class WEBKIT_STORAGE_EXPORT UploadFileSystemFileElementReader + : public net::UploadElementReader { + public: + UploadFileSystemFileElementReader( + FileSystemContext* file_system_context, + const GURL& url, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time); + virtual ~UploadFileSystemFileElementReader(); + + // UploadElementReader overrides: + virtual int Init(const net::CompletionCallback& callback) OVERRIDE; + virtual uint64 GetContentLength() const OVERRIDE; + virtual uint64 BytesRemaining() const OVERRIDE; + virtual int Read(net::IOBuffer* buf, + int buf_length, + const net::CompletionCallback& callback) OVERRIDE; + + private: + void OnGetLength(const net::CompletionCallback& callback, int64 result); + void OnRead(const net::CompletionCallback& callback, int result); + + scoped_refptr<FileSystemContext> file_system_context_; + const GURL url_; + const uint64 range_offset_; + const uint64 range_length_; + const base::Time expected_modification_time_; + + scoped_ptr<webkit_blob::FileStreamReader> stream_reader_; + + uint64 stream_length_; + uint64 position_; + + base::WeakPtrFactory<UploadFileSystemFileElementReader> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(UploadFileSystemFileElementReader); +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_UPLOAD_FILE_SYSTEM_FILE_ELEMENT_READER_H_ diff --git a/webkit/fileapi/upload_file_system_file_element_reader_unittest.cc b/webkit/fileapi/upload_file_system_file_element_reader_unittest.cc new file mode 100644 index 0000000..ba9ce01 --- /dev/null +++ b/webkit/fileapi/upload_file_system_file_element_reader_unittest.cc @@ -0,0 +1,286 @@ +// Copyright (c) 2013 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 "webkit/fileapi/upload_file_system_file_element_reader.h" + +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop.h" +#include "net/base/io_buffer.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_file_util.h" +#include "webkit/fileapi/file_system_operation_context.h" +#include "webkit/fileapi/file_system_task_runners.h" +#include "webkit/fileapi/file_system_url.h" +#include "webkit/fileapi/mock_file_system_options.h" + +namespace fileapi { + +namespace { + +const char kFileSystemURLOrigin[] = "http://remote"; +const fileapi::FileSystemType kFileSystemType = + fileapi::kFileSystemTypeTemporary; + +} // namespace + +class UploadFileSystemFileElementReaderTest : public testing::Test { + public: + UploadFileSystemFileElementReaderTest() + : message_loop_(MessageLoop::TYPE_IO) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + file_system_context_ = new fileapi::FileSystemContext( + fileapi::FileSystemTaskRunners::CreateMockTaskRunners(), + NULL, + NULL, + temp_dir_.path(), + fileapi::CreateDisallowFileAccessOptions()); + + file_system_context_->OpenFileSystem( + GURL(kFileSystemURLOrigin), + kFileSystemType, + true, // create + base::Bind(&UploadFileSystemFileElementReaderTest::OnValidateFileSystem, + base::Unretained(this))); + MessageLoop::current()->RunUntilIdle(); + ASSERT_TRUE(file_system_root_url_.is_valid()); + + // Prepare a file on file system. + const char kTestData[] = "abcdefghijklmnop0123456789"; + file_data_.assign(kTestData, kTestData + arraysize(kTestData) - 1); + const char kFilename[] = "File.dat"; + file_url_ = GetFileSystemURL(kFilename); + WriteFileSystemFile(kFilename, &file_data_[0], file_data_.size(), + &file_modification_time_); + + // Create and initialize a reader. + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, file_url_, 0, kuint64max, + file_modification_time_)); + net::TestCompletionCallback callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(callback.callback())); + EXPECT_EQ(net::OK, callback.WaitForResult()); + EXPECT_EQ(file_data_.size(), reader_->GetContentLength()); + EXPECT_EQ(file_data_.size(), reader_->BytesRemaining()); + EXPECT_FALSE(reader_->IsInMemory()); + } + + protected: + GURL GetFileSystemURL(const std::string& filename) { + return GURL(file_system_root_url_.spec() + filename); + } + + void WriteFileSystemFile(const std::string& filename, + const char* buf, + int buf_size, + base::Time* modification_time) { + fileapi::FileSystemURL url(GURL(kFileSystemURLOrigin), + kFileSystemType, + FilePath().AppendASCII(filename)); + + fileapi::FileSystemFileUtil* file_util = + file_system_context_->GetFileUtil(kFileSystemType); + + fileapi::FileSystemOperationContext context(file_system_context_); + context.set_allowed_bytes_growth(1024); + + base::PlatformFile handle = base::kInvalidPlatformFileValue; + bool created = false; + ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen( + &context, + url, + base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, + &handle, + &created)); + EXPECT_TRUE(created); + ASSERT_NE(base::kInvalidPlatformFileValue, handle); + ASSERT_EQ(buf_size, + base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size)); + base::ClosePlatformFile(handle); + + base::PlatformFileInfo file_info; + FilePath platform_path; + ASSERT_EQ(base::PLATFORM_FILE_OK, + file_util->GetFileInfo(&context, url, &file_info, + &platform_path)); + *modification_time = file_info.last_modified; + } + + void OnValidateFileSystem(base::PlatformFileError result, + const std::string& name, + const GURL& root) { + ASSERT_EQ(base::PLATFORM_FILE_OK, result); + ASSERT_TRUE(root.is_valid()); + file_system_root_url_ = root; + } + + MessageLoop message_loop_; + base::ScopedTempDir temp_dir_; + scoped_refptr<FileSystemContext> file_system_context_; + GURL file_system_root_url_; + std::vector<char> file_data_; + GURL file_url_; + base::Time file_modification_time_; + scoped_ptr<UploadFileSystemFileElementReader> reader_; +}; + +TEST_F(UploadFileSystemFileElementReaderTest, ReadAll) { + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(file_data_.size()); + net::TestCompletionCallback read_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback.callback())); + EXPECT_EQ(buf->size(), read_callback.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); + // Try to read again. + EXPECT_EQ(0, reader_->Read(buf, buf->size(), read_callback.callback())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, ReadPartially) { + const size_t kHalfSize = file_data_.size() / 2; + ASSERT_EQ(file_data_.size(), kHalfSize * 2); + + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(kHalfSize); + + net::TestCompletionCallback read_callback1; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback1.callback())); + EXPECT_EQ(buf->size(), read_callback1.WaitForResult()); + EXPECT_EQ(file_data_.size() - buf->size(), reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + kHalfSize, + buf->data())); + + net::TestCompletionCallback read_callback2; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback2.callback())); + EXPECT_EQ(buf->size(), read_callback2.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin() + kHalfSize, file_data_.end(), + buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, ReadTooMuch) { + const size_t kTooLargeSize = file_data_.size() * 2; + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(kTooLargeSize); + net::TestCompletionCallback read_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback.callback())); + EXPECT_EQ(static_cast<int>(file_data_.size()), read_callback.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, MultipleInit) { + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(file_data_.size()); + + // Read all. + net::TestCompletionCallback read_callback1; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback1.callback())); + EXPECT_EQ(buf->size(), read_callback1.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); + + // Call Init() again to reset the state. + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::OK, init_callback.WaitForResult()); + EXPECT_EQ(file_data_.size(), reader_->GetContentLength()); + EXPECT_EQ(file_data_.size(), reader_->BytesRemaining()); + + // Read again. + net::TestCompletionCallback read_callback2; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback2.callback())); + EXPECT_EQ(buf->size(), read_callback2.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.end(), buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, InitDuringAsyncOperation) { + scoped_refptr<net::IOBufferWithSize> buf = + new net::IOBufferWithSize(file_data_.size()); + + // Start reading all. + net::TestCompletionCallback read_callback1; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback1.callback())); + + // Call Init to cancel the previous read. + net::TestCompletionCallback init_callback1; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback1.callback())); + + // Call Init again to cancel the previous init. + net::TestCompletionCallback init_callback2; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback2.callback())); + EXPECT_EQ(net::OK, init_callback2.WaitForResult()); + EXPECT_EQ(file_data_.size(), reader_->GetContentLength()); + EXPECT_EQ(file_data_.size(), reader_->BytesRemaining()); + + // Read half. + scoped_refptr<net::IOBufferWithSize> buf2 = + new net::IOBufferWithSize(file_data_.size() / 2); + net::TestCompletionCallback read_callback2; + EXPECT_EQ(net::ERR_IO_PENDING, reader_->Read(buf2, buf2->size(), + read_callback2.callback())); + EXPECT_EQ(buf2->size(), read_callback2.WaitForResult()); + EXPECT_EQ(file_data_.size() - buf2->size(), reader_->BytesRemaining()); + EXPECT_TRUE(std::equal(file_data_.begin(), file_data_.begin() + buf2->size(), + buf2->data())); + + // Make sure callbacks are not called for cancelled operations. + EXPECT_FALSE(read_callback1.have_result()); + EXPECT_FALSE(init_callback1.have_result()); +} + +TEST_F(UploadFileSystemFileElementReaderTest, Range) { + const int kOffset = 2; + const int kLength = file_data_.size() - kOffset * 3; + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, file_url_, kOffset, kLength, base::Time())); + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::OK, init_callback.WaitForResult()); + EXPECT_EQ(static_cast<uint64>(kLength), reader_->GetContentLength()); + EXPECT_EQ(static_cast<uint64>(kLength), reader_->BytesRemaining()); + scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(kLength); + net::TestCompletionCallback read_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Read(buf, buf->size(), + read_callback.callback())); + EXPECT_EQ(kLength, read_callback.WaitForResult()); + EXPECT_TRUE(std::equal(file_data_.begin() + kOffset, + file_data_.begin() + kOffset + kLength, + buf->data())); +} + +TEST_F(UploadFileSystemFileElementReaderTest, FileChanged) { + // Expect one second before the actual modification time to simulate change. + const base::Time expected_modification_time = + file_modification_time_ - base::TimeDelta::FromSeconds(1); + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, file_url_, 0, kuint64max, + expected_modification_time)); + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, init_callback.WaitForResult()); +} + +TEST_F(UploadFileSystemFileElementReaderTest, WrongURL) { + const GURL wrong_url = GetFileSystemURL("wrong_file_name.dat"); + reader_.reset(new UploadFileSystemFileElementReader( + file_system_context_, wrong_url, 0, kuint64max, base::Time())); + net::TestCompletionCallback init_callback; + ASSERT_EQ(net::ERR_IO_PENDING, reader_->Init(init_callback.callback())); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, init_callback.WaitForResult()); +} + +} // namespace fileapi diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index 69162bf..39288ee 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -106,6 +106,8 @@ '../fileapi/task_runner_bound_observer_list.h', '../fileapi/test_mount_point_provider.cc', '../fileapi/test_mount_point_provider.h', + '../fileapi/upload_file_system_file_element_reader.cc', + '../fileapi/upload_file_system_file_element_reader.h', '../fileapi/webfilewriter_base.cc', '../fileapi/webfilewriter_base.h', ], |