Chromium Code Reviews| Index: chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc |
| diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e87bab3ae1238dcab5cba0f9f43da6cc5226eb88 |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc |
| @@ -0,0 +1,316 @@ |
| +// Copyright 2014 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 "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h" |
| + |
| +#include "base/files/file.h" |
| +#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h" |
| +#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" |
| +#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "net/base/net_errors.h" |
| + |
| +using content::BrowserThread; |
| + |
| +namespace chromeos { |
| +namespace file_system_provider { |
| +namespace { |
| + |
| +// Dicards the callback from CloseFile(). |
| +void EmptyStatusCallback(base::File::Error /* result */) { |
| +} |
| + |
| +// Converts net::CompletionCallback to net::Int64CompletionCallback. |
| +void Int64ToIntCompletionCallback(net::CompletionCallback callback, |
| + int64 result) { |
| + callback.Run(static_cast<int>(result)); |
| +} |
| + |
| +// Opens a file for reading and calls the completion callback. Must be called |
| +// on UI thread. |
| +void OpenFileOnUIThread( |
| + const fileapi::FileSystemURL& url, |
| + const FileStreamReader::InitializeCompletedCallback& callback) { |
| + // TODO(mtomasz): Check if the modification time of the file is as expected. |
| + util::FileSystemURLParser parser(url); |
| + if (!parser.Parse()) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(callback, |
| + base::WeakPtr<ProvidedFileSystemInterface>(), |
| + base::FilePath(), |
| + 0 /* file_handle */, |
| + base::File::FILE_ERROR_SECURITY)); |
| + return; |
| + } |
| + |
| + parser.file_system()->OpenFile( |
| + parser.file_path(), |
| + ProvidedFileSystemInterface::OPEN_FILE_MODE_READ, |
| + false /* create */, |
| + base::Bind( |
| + callback, parser.file_system()->GetWeakPtr(), parser.file_path())); |
| +} |
| + |
| +// Forwards results of calling OpenFileOnUIThread back to the IO thread. |
| +void OnOpenFileCompletedOnUIThread( |
| + const FileStreamReader::InitializeCompletedCallback& callback, |
| + base::WeakPtr<ProvidedFileSystemInterface> file_system, |
| + const base::FilePath& file_path, |
| + int file_handle, |
| + base::File::Error result) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(callback, file_system, file_path, file_handle, result)); |
| +} |
| + |
| +// Closes a file. Ignores result, since it is called from a constructor. |
| +// Must be called on UI thread. |
| +void CloseFileOnUIThread(base::WeakPtr<ProvidedFileSystemInterface> file_system, |
| + int file_handle) { |
| + if (file_system.get()) |
| + file_system->CloseFile(file_handle, base::Bind(&EmptyStatusCallback)); |
| +} |
| + |
| +// Requests reading contents of a file. In case of either success or a failure |
| +// |callback| is executed. It can be called many times, until |has_next| is set |
| +// to false. This function guarantees that it will succeed only if the file has |
| +// not been changed while reading. Must be called on UI thread. |
| +void ReadFileOnUIThread( |
| + base::WeakPtr<ProvidedFileSystemInterface> file_system, |
| + int file_handle, |
| + net::IOBuffer* buffer, |
| + int64 offset, |
| + int length, |
| + const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) { |
| + // If the file system got unmounted, then abort the reading operation. |
| + if (!file_system.get()) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind( |
| + callback, 0, false /* has_next */, base::File::FILE_ERROR_ABORT)); |
| + return; |
| + } |
| + |
| + file_system->ReadFile(file_handle, buffer, offset, length, callback); |
| +} |
| + |
| +// Forward the completion callback to IO thread. |
| +void OnReadChunkReceivedOnUIThread( |
| + const ProvidedFileSystemInterface::ReadChunkReceivedCallback& |
| + chunk_received_callback, |
| + int chunk_length, |
| + bool has_next, |
| + base::File::Error result) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(chunk_received_callback, chunk_length, has_next, result)); |
| +} |
| + |
| +// TODO(mtomasz): Comment. |
| +void GetMetadataOnUIThread( |
| + base::WeakPtr<ProvidedFileSystemInterface> file_system, |
| + const base::FilePath& file_path, |
| + const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) { |
| + // If the file system got unmounted, then abort the get length operation. |
| + if (!file_system.get()) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(callback, base::File::FILE_ERROR_ABORT, base::File::Info())); |
| + return; |
| + } |
| + |
| + file_system->GetMetadata(file_path, callback); |
| +} |
| + |
| +// Forward the completion callback to IO thread. |
| +void OnGetMetadataReceivedOnUIThread( |
| + const fileapi::AsyncFileUtil::GetFileInfoCallback& callback, |
| + base::File::Error result, |
| + const base::File::Info& file_info) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, base::Bind(callback, result, file_info)); |
| +} |
| + |
| +} // namespace |
| + |
| +FileStreamReader::FileStreamReader(fileapi::FileSystemContext* context, |
| + const fileapi::FileSystemURL& url, |
| + int64 initial_offset, |
| + const base::Time& expected_modification_time) |
| + : context_(context), |
| + url_(url), |
| + current_offset_(initial_offset), |
| + current_length_(0), |
| + expected_modification_time_(expected_modification_time), |
| + file_handle_(0), |
| + weak_ptr_factory_(this) { |
| +} |
| + |
| +FileStreamReader::~FileStreamReader() { |
| + CloseFileOnUIThread(file_system_, file_handle_); |
| +} |
| + |
| +void FileStreamReader::Initialize( |
| + const base::Closure& pending_closure, |
| + const net::Int64CompletionCallback& error_callback) { |
| + OpenFileOnUIThread( |
| + url_, |
| + base::Bind(&OnOpenFileCompletedOnUIThread, |
| + base::Bind(&FileStreamReader::OnInitializeCompleted, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + pending_closure, |
| + error_callback))); |
| +} |
| + |
| +void FileStreamReader::OnInitializeCompleted( |
| + const base::Closure& pending_closure, |
| + const net::Int64CompletionCallback& error_callback, |
| + base::WeakPtr<ProvidedFileSystemInterface> file_system, |
| + const base::FilePath& file_path, |
| + int file_handle, |
| + base::File::Error result) { |
| + // In case of an error, return immediately using the |error_callback| of the |
| + // Read() or GetLength() pending request. |
| + if (result != base::File::FILE_OK) { |
| + error_callback.Run(net::FileErrorToNetError(result)); |
| + return; |
| + } |
| + |
| + file_system_ = file_system; |
| + file_path_ = file_path; |
| + file_handle_ = file_handle; |
| + DCHECK_LT(0, file_handle); |
| + |
| + // Run the task waiting for the initialization to be completed. |
| + pending_closure.Run(); |
| +} |
| + |
| +int FileStreamReader::Read(net::IOBuffer* buffer, |
| + int buffer_length, |
| + const net::CompletionCallback& callback) { |
| + // Lazily initialize with the first call to Read(). |
| + if (!file_system_.get()) { |
| + Initialize(base::Bind(&FileStreamReader::ReadAfterInitialized, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + buffer, |
| + buffer_length, |
| + callback), |
| + base::Bind(&Int64ToIntCompletionCallback, callback)); |
| + return net::ERR_IO_PENDING; |
| + } |
| + |
| + ReadAfterInitialized(buffer, buffer_length, callback); |
| + return net::ERR_IO_PENDING; |
| +} |
| + |
| +int64 FileStreamReader::GetLength( |
| + const net::Int64CompletionCallback& callback) { |
| + // Lazily initialize with the first call to Read(). |
| + if (!file_system_.get()) { |
| + Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + callback), |
| + callback); |
| + return net::ERR_IO_PENDING; |
| + } |
| + |
| + GetLengthAfterInitialized(callback); |
| + return net::ERR_IO_PENDING; |
| +} |
| + |
| +void FileStreamReader::ReadAfterInitialized( |
| + net::IOBuffer* buffer, |
| + int buffer_length, |
| + const net::CompletionCallback& callback) { |
| + // If the file system got unmounted, then abort the reading operation. |
| + if (!file_system_.get()) { |
| + callback.Run(net::ERR_ABORTED); |
| + return; |
| + } |
| + |
| + current_length_ = 0; |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&ReadFileOnUIThread, |
| + file_system_, |
| + file_handle_, |
| + buffer, |
| + current_offset_, |
| + buffer_length, |
| + base::Bind(&OnReadChunkReceivedOnUIThread, |
| + base::Bind(&FileStreamReader::OnReadChunkReceived, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + callback)))); |
| +} |
| + |
| +void FileStreamReader::GetLengthAfterInitialized( |
| + const net::Int64CompletionCallback& callback) { |
| + // If the file system got unmounted, then abort the length fetching operation. |
| + if (!file_system_.get()) { |
| + callback.Run(net::ERR_ABORTED); |
| + return; |
| + } |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind( |
| + &GetMetadataOnUIThread, |
| + file_system_, |
| + file_path_, |
| + base::Bind( |
| + &OnGetMetadataReceivedOnUIThread, |
| + base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + callback)))); |
| +} |
| + |
| +void FileStreamReader::OnReadChunkReceived( |
| + const net::CompletionCallback& callback, |
| + int chunk_length, |
| + bool has_next, |
| + base::File::Error result) { |
| + current_length_ += chunk_length; |
| + |
| + |
|
hirono
2014/05/16 06:24:48
nit: The line is added unintentionally?
mtomasz
2014/05/16 06:31:11
Done.
|
| + // If this is the last chunk with a success, then finalize. |
| + if (!has_next && result == base::File::FILE_OK) { |
| + current_offset_ += current_length_; |
| + callback.Run(current_length_); |
| + return; |
| + } |
| + |
| + // In case of an error, abort. |
| + if (result != base::File::FILE_OK) { |
| + DCHECK(!has_next); |
| + callback.Run(net::FileErrorToNetError(result)); |
| + } |
| + |
| + // More data is about to come, so do not call the callback yet. |
| + DCHECK(has_next); |
| +} |
| + |
| +void FileStreamReader::OnGetMetadataForGetLengthReceived( |
| + const net::Int64CompletionCallback& callback, |
| + base::File::Error result, |
| + const base::File::Info& file_info) { |
| + // In case of an error, abort. |
| + if (result != base::File::FILE_OK) { |
| + callback.Run(net::FileErrorToNetError(result)); |
| + return; |
| + } |
| + |
| + DCHECK_EQ(result, base::File::FILE_OK); |
| + callback.Run(file_info.size); |
| +} |
| + |
| +} // namespace file_system_provider |
| +} // namespace chromeos |