summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos
diff options
context:
space:
mode:
authorhidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-23 01:14:21 +0000
committerhidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-23 01:14:21 +0000
commit1b2fc951d33ce93f4ba12743c6a8f405a2418126 (patch)
treee1ede187fe56cfbfc7ebd3e97f9ac96747042f37 /chrome/browser/chromeos
parent0852f829239a64c744dafdd29ffdc0f555b78a69 (diff)
downloadchromium_src-1b2fc951d33ce93f4ba12743c6a8f405a2418126.zip
chromium_src-1b2fc951d33ce93f4ba12743c6a8f405a2418126.tar.gz
chromium_src-1b2fc951d33ce93f4ba12743c6a8f405a2418126.tar.bz2
Implement DriveFileStreamReader.
Simplified the interface by getting rid of the inheritance of webkit_blog::FileStreamReader. BUG=127129 TEST=Ran unit_tests. Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=195504 Review URL: https://chromiumcodereview.appspot.com/14365018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195681 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
-rw-r--r--chrome/browser/chromeos/drive/drive_file_stream_reader.cc176
-rw-r--r--chrome/browser/chromeos/drive/drive_file_stream_reader.h84
-rw-r--r--chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc142
3 files changed, 380 insertions, 22 deletions
diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader.cc b/chrome/browser/chromeos/drive/drive_file_stream_reader.cc
index 66e52e3..2a20415 100644
--- a/chrome/browser/chromeos/drive/drive_file_stream_reader.cc
+++ b/chrome/browser/chromeos/drive/drive_file_stream_reader.cc
@@ -9,6 +9,9 @@
#include "base/callback_helpers.h"
#include "base/logging.h"
+#include "chrome/browser/chromeos/drive/drive.pb.h"
+#include "chrome/browser/chromeos/drive/drive_file_system_interface.h"
+#include "chrome/browser/google_apis/task_util.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
@@ -166,25 +169,178 @@ void NetworkReaderProxy::OnError(DriveFileError error) {
} // namespace internal
+namespace {
+
+// Calls DriveFileSystemInterface::GetFileContentByPath if the file system
+// is available. If not, the |completion_callback| is invoked with
+// DRIVE_FILE_ERROR_FAILED.
+void GetFileContentByPathOnUIThread(
+ const DriveFileStreamReader::DriveFileSystemGetter& file_system_getter,
+ const base::FilePath& drive_file_path,
+ const GetFileContentInitializedCallback& initialized_callback,
+ const google_apis::GetContentCallback& get_content_callback,
+ const FileOperationCallback& completion_callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ DriveFileSystemInterface* file_system = file_system_getter.Run();
+ if (!file_system) {
+ completion_callback.Run(DRIVE_FILE_ERROR_FAILED);
+ return;
+ }
-DriveFileStreamReader::DriveFileStreamReader() {
+ file_system->GetFileContentByPath(drive_file_path,
+ initialized_callback,
+ get_content_callback,
+ completion_callback);
+}
+
+// Helper to run DriveFileSystemInterface::GetFileContentByPath on UI thread.
+void GetFileContentByPath(
+ const DriveFileStreamReader::DriveFileSystemGetter& file_system_getter,
+ const base::FilePath& drive_file_path,
+ const GetFileContentInitializedCallback& initialized_callback,
+ const google_apis::GetContentCallback& get_content_callback,
+ const FileOperationCallback& completion_callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GetFileContentByPathOnUIThread,
+ file_system_getter,
+ drive_file_path,
+ google_apis::CreateRelayCallback(initialized_callback),
+ google_apis::CreateRelayCallback(get_content_callback),
+ google_apis::CreateRelayCallback(completion_callback)));
+}
+
+} // namespace
+
+DriveFileStreamReader::DriveFileStreamReader(
+ const DriveFileSystemGetter& drive_file_system_getter)
+ : drive_file_system_getter_(drive_file_system_getter),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
}
DriveFileStreamReader::~DriveFileStreamReader() {
}
-int DriveFileStreamReader::Read(net::IOBuffer* buf, int buf_len,
+void DriveFileStreamReader::Initialize(
+ const base::FilePath& drive_file_path,
+ const InitializeCompletionCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!callback.is_null());
+
+ GetFileContentByPath(
+ drive_file_system_getter_,
+ drive_file_path,
+ base::Bind(&DriveFileStreamReader
+ ::InitializeAfterGetFileContentByPathInitialized,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback),
+ base::Bind(&DriveFileStreamReader::OnGetContent,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&DriveFileStreamReader::OnGetFileContentByPathCompletion,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback));
+}
+
+int DriveFileStreamReader::Read(net::IOBuffer* buffer, int buffer_length,
const net::CompletionCallback& callback) {
- // TODO(hidehiko): Implement this.
- NOTIMPLEMENTED();
- return 0;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(reader_proxy_);
+ DCHECK(buffer);
+ DCHECK(!callback.is_null());
+ return reader_proxy_->Read(buffer, buffer_length, callback);
+}
+
+void DriveFileStreamReader::InitializeAfterGetFileContentByPathInitialized(
+ const InitializeCompletionCallback& callback,
+ DriveFileError error,
+ scoped_ptr<DriveEntryProto> entry,
+ const base::FilePath& drive_file_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (error != DRIVE_FILE_OK) {
+ callback.Run(error, scoped_ptr<DriveEntryProto>());
+ return;
+ }
+ DCHECK(entry);
+
+ if (drive_file_path.empty()) {
+ // The file is not cached, and being downloaded.
+ reader_proxy_.reset(
+ new internal::NetworkReaderProxy(entry->file_info().size()));
+ callback.Run(DRIVE_FILE_OK, entry.Pass());
+ return;
+ }
+
+ // Otherwise, open the stream for file.
+ scoped_ptr<net::FileStream> file_stream(new net::FileStream(NULL));
+ net::FileStream* file_stream_ptr = file_stream.get();
+ net::CompletionCallback open_completion_callback = base::Bind(
+ &DriveFileStreamReader::InitializeAfterLocalFileOpen,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ base::Passed(&entry),
+ base::Passed(&file_stream));
+ int result = file_stream_ptr->Open(
+ drive_file_path,
+ base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_ASYNC,
+ open_completion_callback);
+
+ if (result == net::ERR_IO_PENDING) {
+ // If the result ERR_IO_PENDING, the callback will be invoked later.
+ // Do nothing here.
+ return;
+ }
+
+ open_completion_callback.Run(result);
}
-int64 DriveFileStreamReader::GetLength(
- const net::Int64CompletionCallback& callback) {
- // TODO(hidehiko): Implement this.
- NOTIMPLEMENTED();
- return 0;
+void DriveFileStreamReader::InitializeAfterLocalFileOpen(
+ const InitializeCompletionCallback& callback,
+ scoped_ptr<DriveEntryProto> entry,
+ scoped_ptr<net::FileStream> file_stream,
+ int open_result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (open_result != net::OK) {
+ callback.Run(DRIVE_FILE_ERROR_FAILED, scoped_ptr<DriveEntryProto>());
+ return;
+ }
+
+ reader_proxy_.reset(new internal::LocalReaderProxy(file_stream.Pass()));
+ callback.Run(DRIVE_FILE_OK, entry.Pass());
+}
+
+void DriveFileStreamReader::OnGetContent(google_apis::GDataErrorCode error_code,
+ scoped_ptr<std::string> data) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(reader_proxy_);
+ reader_proxy_->OnGetContent(data.Pass());
+}
+
+void DriveFileStreamReader::OnGetFileContentByPathCompletion(
+ const InitializeCompletionCallback& callback,
+ DriveFileError error) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (error == DRIVE_FILE_OK) {
+ // We are interested in only errors.
+ return;
+ }
+
+ if (reader_proxy_) {
+ // If the proxy object available, send the error to it.
+ reader_proxy_->OnError(error);
+ } else {
+ // Here, this callback is invoked in the initialization process.
+ // So let the client know via initialization callback.
+ callback.Run(error, scoped_ptr<DriveEntryProto>());
+ }
}
} // namespace drive
diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader.h b/chrome/browser/chromeos/drive/drive_file_stream_reader.h
index 8100fcd..f69fdaf 100644
--- a/chrome/browser/chromeos/drive/drive_file_stream_reader.h
+++ b/chrome/browser/chromeos/drive/drive_file_stream_reader.h
@@ -8,11 +8,12 @@
#include <string>
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "chrome/browser/chromeos/drive/drive_file_error.h"
+#include "chrome/browser/google_apis/gdata_errorcode.h"
#include "net/base/completion_callback.h"
-#include "webkit/blob/file_stream_reader.h"
namespace net {
class FileStream;
@@ -92,23 +93,82 @@ class NetworkReaderProxy : public ReaderProxy {
} // namespace internal
-// TODO(hidehiko): Simplify the interface by getting rid of
-// webkit_blob::FileStreamReader inheritance.
-class DriveFileStreamReader : public webkit_blob::FileStreamReader {
- public:
- DriveFileStreamReader();
- virtual ~DriveFileStreamReader();
+class DriveFileSystemInterface;
+class DriveEntryProto;
- // webkit_blob::FileStreamReader overrides.
- virtual int Read(net::IOBuffer* buf, int buf_len,
- const net::CompletionCallback& callback) OVERRIDE;
- virtual int64 GetLength(
- const net::Int64CompletionCallback& callback) OVERRIDE;
+// The stream reader for a file in DriveFileSystem. Instances of this class
+// should live on IO thread.
+class DriveFileStreamReader {
+ public:
+ // Callback to return the DriveFileSystemInterface instance. This is an
+ // injecting point for testing.
+ // Note that the callback will be copied between threads (IO and UI), and
+ // will be called on UI thread.
+ typedef base::Callback<DriveFileSystemInterface*()> DriveFileSystemGetter;
+
+ // Callback to return the result of Initialize().
+ typedef base::Callback<void(DriveFileError error,
+ scoped_ptr<DriveEntryProto> entry)>
+ InitializeCompletionCallback;
+
+ explicit DriveFileStreamReader(
+ const DriveFileSystemGetter& drive_file_system_getter);
+ ~DriveFileStreamReader();
+
+ // Initializes the stream for the |drive_file_path|.
+ // |callback| must not be null.
+ // TODO(hidehiko): Support reading range (crbug.com/168258).
+ void Initialize(const base::FilePath& drive_file_path,
+ const InitializeCompletionCallback& callback);
+
+ // Reads the data into |buffer| at most |buffer_length|, and returns
+ // the number of bytes. If an error happened, returns an error code.
+ // If no data is available yet, returns net::ERR_IO_PENDING immediately,
+ // and when the data is available the actual Read operation is done
+ // and |callback| will be run with the result.
+ // The Read() method must not be called before the Initialize() is completed
+ // successfully, or if there is pending read operation.
+ // Neither |buffer| nor |callback| must be null.
+ int Read(net::IOBuffer* buffer, int buffer_length,
+ const net::CompletionCallback& callback);
private:
+ // Part of Initialize. Called after GetFileContentByPath's initialization
+ // is done.
+ void InitializeAfterGetFileContentByPathInitialized(
+ const InitializeCompletionCallback& callback,
+ DriveFileError error,
+ scoped_ptr<DriveEntryProto> entry,
+ const base::FilePath& drive_file_path);
+
+ // Part of Initialize. Called when the local file open process is done.
+ void InitializeAfterLocalFileOpen(
+ const InitializeCompletionCallback& callback,
+ scoped_ptr<DriveEntryProto> entry,
+ scoped_ptr<net::FileStream> file_stream,
+ int open_result);
+
+ // Called when the data is received from the server.
+ void OnGetContent(google_apis::GDataErrorCode error_code,
+ scoped_ptr<std::string> data);
+
+ // Called when GetFileContentByPath is completed.
+ void OnGetFileContentByPathCompletion(
+ const InitializeCompletionCallback& callback,
+ DriveFileError error);
+
+ const DriveFileSystemGetter drive_file_system_getter_;
+ scoped_ptr<internal::ReaderProxy> reader_proxy_;
+
+ // This should remain the last member so it'll be destroyed first and
+ // invalidate its weak pointers before other members are destroyed.
+ base::WeakPtrFactory<DriveFileStreamReader> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DriveFileStreamReader);
};
+// TODO(hidehiko): Add thin wrapper class inheriting
+// webkit_blob::FileStreamReader for the DriveFileStreamReader.
+
} // namespace drive
#endif // CHROME_BROWSER_CHROMEOS_DRIVE_DRIVE_FILE_STREAM_READER_H_
diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc b/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc
index 37f36b3..c24de06 100644
--- a/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc
+++ b/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc
@@ -6,9 +6,14 @@
#include <string>
+#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/message_loop.h"
+#include "chrome/browser/chromeos/drive/drive_file_system_util.h"
+#include "chrome/browser/chromeos/drive/fake_drive_file_system.h"
+#include "chrome/browser/google_apis/fake_drive_service.h"
+#include "chrome/browser/google_apis/task_util.h"
#include "chrome/browser/google_apis/test_util.h"
#include "content/public/test/test_browser_thread.h"
#include "net/base/file_stream.h"
@@ -182,4 +187,141 @@ TEST(NetworkReaderProxyTest, ErrorWithPendingData) {
}
} // namespace internal
+
+class DriveFileStreamReaderTest : public ::testing::Test {
+ protected:
+ DriveFileStreamReaderTest()
+ : ui_thread_(BrowserThread::UI),
+ io_thread_(BrowserThread::IO, &message_loop_) {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ ui_thread_.Start();
+
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DriveFileStreamReaderTest::SetUpOnUIThread,
+ base::Unretained(this)),
+ base::MessageLoop::QuitClosure());
+ message_loop_.Run();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DriveFileStreamReaderTest::TearDownOnUIThread,
+ base::Unretained(this)),
+ base::MessageLoop::QuitClosure());
+ message_loop_.Run();
+ }
+
+ void SetUpOnUIThread() {
+ // Initialize FakeDriveService.
+ fake_drive_service_.reset(new google_apis::FakeDriveService);
+ fake_drive_service_->LoadResourceListForWapi(
+ "chromeos/gdata/root_feed.json");
+ fake_drive_service_->LoadAccountMetadataForWapi(
+ "chromeos/gdata/account_metadata.json");
+ fake_drive_service_->LoadAppListForDriveApi("chromeos/drive/applist.json");
+
+ // Create a testee instance.
+ fake_drive_file_system_.reset(
+ new test_util::FakeDriveFileSystem(fake_drive_service_.get()));
+ fake_drive_file_system_->Initialize();
+ }
+
+ void TearDownOnUIThread() {
+ fake_drive_file_system_.reset();
+ fake_drive_service_.reset();
+ }
+
+ DriveFileSystemInterface* GetDriveFileSystem() {
+ return fake_drive_file_system_.get();
+ }
+
+ DriveFileStreamReader::DriveFileSystemGetter GetDriveFileSystemGetter() {
+ return base::Bind(&DriveFileStreamReaderTest::GetDriveFileSystem,
+ base::Unretained(this));
+ }
+
+ MessageLoopForIO message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread io_thread_;
+
+ scoped_ptr<google_apis::FakeDriveService> fake_drive_service_;
+ scoped_ptr<test_util::FakeDriveFileSystem> fake_drive_file_system_;
+};
+
+TEST_F(DriveFileStreamReaderTest, Read) {
+ const base::FilePath kDriveFile =
+ util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt");
+ net::TestCompletionCallback callback;
+ const int kBufferSize = 3;
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+
+ // Create the reader, and initialize it.
+ // In this case, the file is not yet locally cached.
+ scoped_ptr<DriveFileStreamReader> reader(
+ new DriveFileStreamReader(GetDriveFileSystemGetter()));
+
+ DriveFileError error = DRIVE_FILE_ERROR_FAILED;
+ scoped_ptr<DriveEntryProto> entry;
+ reader->Initialize(
+ kDriveFile,
+ google_apis::CreateComposedCallback(
+ base::Bind(&google_apis::test_util::RunAndQuit),
+ google_apis::test_util::CreateCopyResultCallback(
+ &error, &entry)));
+ message_loop_.Run();
+ EXPECT_EQ(DRIVE_FILE_OK, error);
+ ASSERT_TRUE(entry);
+
+ // Read data from the reader.
+ size_t content_size = entry->file_info().size();
+ std::string first_content;
+ while (first_content.size() < content_size) {
+ int result = reader->Read(buffer.get(), kBufferSize, callback.callback());
+ result = callback.GetResult(result);
+ ASSERT_GT(result, 0);
+ first_content.append(buffer->data(), result);
+ }
+
+ EXPECT_EQ(content_size, first_content.size());
+
+ // Create second instance and initialize it.
+ // In this case, the file should be cached one.
+ reader.reset(
+ new DriveFileStreamReader(GetDriveFileSystemGetter()));
+
+ error = DRIVE_FILE_ERROR_FAILED;
+ entry.reset();
+ reader->Initialize(
+ kDriveFile,
+ google_apis::CreateComposedCallback(
+ base::Bind(&google_apis::test_util::RunAndQuit),
+ google_apis::test_util::CreateCopyResultCallback(
+ &error, &entry)));
+ message_loop_.Run();
+ EXPECT_EQ(DRIVE_FILE_OK, error);
+ ASSERT_TRUE(entry);
+
+ // The size should be same.
+ EXPECT_EQ(content_size,
+ static_cast<size_t>(entry->file_info().size()));
+
+ // Read data from the reader, again.
+ std::string second_content;
+ while (second_content.size() < content_size) {
+ int result = reader->Read(buffer.get(), kBufferSize, callback.callback());
+ result = callback.GetResult(result);
+ ASSERT_GT(result, 0);
+ second_content.append(buffer->data(), result);
+ }
+
+ // The same content is expected.
+ EXPECT_EQ(first_content, second_content);
+}
+
} // namespace drive