summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-22 01:26:31 +0000
committermtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-22 01:26:31 +0000
commit839b0795344532853f699161c44b69a454397366 (patch)
tree20908d8509127fd3ee35f93baf4f290711cea794
parent0c5703d1a2552eb3d15a7c589f34e3acb6bfc881 (diff)
downloadchromium_src-839b0795344532853f699161c44b69a454397366.zip
chromium_src-839b0795344532853f699161c44b69a454397366.tar.gz
chromium_src-839b0795344532853f699161c44b69a454397366.tar.bz2
[fsp] Add FileStreamReader for the reading operation.
This patch adds a FileStreamReader implementation for provided file systeme. Note, that this is an initial version, which doesn't validate modification time. That will be added in a separate patch. TEST=browser_tests: *FileSystemProvider*ReadFile*, unit_tests: *FileSystemProvider*FileStreamReader* BUG=248427 R=benwells@chromium.org, hirono@chromium.org, kinaba@chromium.org Review URL: https://codereview.chromium.org/288113004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272041 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc6
-rw-r--r--chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc110
-rw-r--r--chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h14
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc6
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc330
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h103
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc294
-rw-r--r--chrome/browser/chromeos/file_system_provider/operations/read_file.cc2
-rw-r--r--chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc38
-rw-r--r--chrome/browser/chromeos/file_system_provider/provided_file_system.cc12
-rw-r--r--chrome/browser/chromeos/file_system_provider/provided_file_system.h3
-rw-r--r--chrome/browser/chromeos/file_system_provider/provided_file_system_info.cc2
-rw-r--r--chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h4
-rw-r--r--chrome/browser/chromeos/fileapi/file_system_backend.cc3
-rw-r--r--chrome/chrome_browser_chromeos.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/extensions/api/file_system_provider.idl3
-rw-r--r--chrome/common/extensions/api/file_system_provider_internal.idl2
-rw-r--r--chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js4
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json18
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/read_file/test.js292
21 files changed, 1185 insertions, 64 deletions
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
index db78129..1cf3ea8 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
@@ -41,4 +41,10 @@ IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, ReadDirectory) {
<< message_;
}
+IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, ReadFile) {
+ ASSERT_TRUE(RunPlatformAppTestWithFlags("file_system_provider/read_file",
+ kFlagLoadAsComponent))
+ << message_;
+}
+
} // namespace extensions
diff --git a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc
index f8fd16e..52e1c68 100644
--- a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc
+++ b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.cc
@@ -31,9 +31,17 @@ void AddDirectoryEntry(fileapi::AsyncFileUtil::EntryList* entry_list,
} // namespace
+const char kFakeFileName[] = "hello.txt";
+const char kFakeFilePath[] = "/hello.txt";
+const char kFakeFileText[] =
+ "This is a testing file. Lorem ipsum dolor sit amet est.";
+const size_t kFakeFileSize = sizeof(kFakeFileText) - 1u;
+
FakeProvidedFileSystem::FakeProvidedFileSystem(
const ProvidedFileSystemInfo& file_system_info)
- : file_system_info_(file_system_info), last_file_handle_(0) {
+ : file_system_info_(file_system_info),
+ last_file_handle_(0),
+ weak_ptr_factory_(this) {
}
FakeProvidedFileSystem::~FakeProvidedFileSystem() {}
@@ -47,27 +55,42 @@ void FakeProvidedFileSystem::RequestUnmount(
void FakeProvidedFileSystem::GetMetadata(
const base::FilePath& entry_path,
const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) {
- // Return fake metadata for the root directory only.
- if (entry_path.AsUTF8Unsafe() != "/") {
+ if (entry_path.AsUTF8Unsafe() == "/") {
+ base::File::Info file_info;
+ file_info.size = 0;
+ file_info.is_directory = true;
+ file_info.is_symbolic_link = false;
+ base::Time last_modified_time;
+ const bool result = base::Time::FromString("Thu Apr 24 00:46:52 UTC 2014",
+ &last_modified_time);
+ DCHECK(result);
+ file_info.last_modified = last_modified_time;
+
base::MessageLoopProxy::current()->PostTask(
- FROM_HERE,
- base::Bind(
- callback, base::File::FILE_ERROR_NOT_FOUND, base::File::Info()));
+ FROM_HERE, base::Bind(callback, base::File::FILE_OK, file_info));
return;
}
- base::File::Info file_info;
- file_info.size = 0;
- file_info.is_directory = true;
- file_info.is_symbolic_link = false;
- base::Time last_modified_time;
- const bool result = base::Time::FromString("Thu Apr 24 00:46:52 UTC 2014",
- &last_modified_time);
- DCHECK(result);
- file_info.last_modified = last_modified_time;
+ if (entry_path.AsUTF8Unsafe() == kFakeFilePath) {
+ base::File::Info file_info;
+ file_info.size = kFakeFileSize;
+ file_info.is_directory = false;
+ file_info.is_symbolic_link = false;
+ base::Time last_modified_time;
+ const bool result = base::Time::FromString("Fri Apr 25 01:47:53 UTC 2014",
+ &last_modified_time);
+ DCHECK(result);
+ file_info.last_modified = last_modified_time;
+
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE, base::Bind(callback, base::File::FILE_OK, file_info));
+ return;
+ }
base::MessageLoopProxy::current()->PostTask(
- FROM_HERE, base::Bind(callback, base::File::FILE_OK, file_info));
+ FROM_HERE,
+ base::Bind(
+ callback, base::File::FILE_ERROR_NOT_FOUND, base::File::Info()));
}
void FakeProvidedFileSystem::ReadDirectory(
@@ -87,9 +110,9 @@ void FakeProvidedFileSystem::ReadDirectory(
{
fileapi::AsyncFileUtil::EntryList entry_list;
AddDirectoryEntry(&entry_list,
- "hello.txt",
+ kFakeFileName,
fileapi::DirectoryEntry::FILE,
- 1024 /* size */,
+ kFakeFileSize,
"Thu Apr 24 00:46:52 UTC 2014");
AddDirectoryEntry(&entry_list,
@@ -130,14 +153,14 @@ void FakeProvidedFileSystem::OpenFile(const base::FilePath& file_path,
}
const int file_handle = ++last_file_handle_;
- opened_files_.insert(file_handle);
+ opened_files_[file_handle] = file_path;
callback.Run(file_handle, base::File::FILE_OK);
}
void FakeProvidedFileSystem::CloseFile(
int file_handle,
const fileapi::AsyncFileUtil::StatusCallback& callback) {
- const std::set<int>::iterator opened_file_it =
+ const OpenedFilesMap::iterator opened_file_it =
opened_files_.find(file_handle);
if (opened_file_it == opened_files_.end()) {
base::MessageLoopProxy::current()->PostTask(
@@ -156,13 +179,41 @@ void FakeProvidedFileSystem::ReadFile(
int64 offset,
int length,
const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) {
- // TODO(mtomasz): Implement together with the FileStreamReader.
- base::MessageLoopProxy::current()->PostTask(
- FROM_HERE,
- base::Bind(callback,
- 0 /* chunk_length */,
- false /* has_next */,
- base::File::FILE_ERROR_SECURITY));
+ const OpenedFilesMap::iterator opened_file_it =
+ opened_files_.find(file_handle);
+ if (opened_file_it == opened_files_.end() ||
+ opened_file_it->second.AsUTF8Unsafe() != kFakeFilePath) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ 0 /* chunk_length */,
+ false /* has_next */,
+ base::File::FILE_ERROR_SECURITY));
+ return;
+ }
+
+ // Send the response byte by byte.
+ size_t current_offset = static_cast<size_t>(offset);
+ size_t current_length = static_cast<size_t>(length);
+
+ // Reading behind EOF, is fine, it will just read 0 bytes.
+ if (current_offset > kFakeFileSize || !current_length) {
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ 0 /* chunk_length */,
+ false /* has_next */,
+ base::File::FILE_OK));
+ }
+
+ while (current_offset < kFakeFileSize && current_length) {
+ buffer->data()[current_offset - offset] = kFakeFileText[current_offset];
+ const bool has_next =
+ (current_offset + 1 < kFakeFileSize) && (current_length - 1);
+ callback.Run(1, has_next, base::File::FILE_OK);
+ current_offset++;
+ current_length--;
+ }
}
const ProvidedFileSystemInfo& FakeProvidedFileSystem::GetFileSystemInfo()
@@ -181,5 +232,10 @@ ProvidedFileSystemInterface* FakeProvidedFileSystem::Create(
return new FakeProvidedFileSystem(file_system_info);
}
+base::WeakPtr<ProvidedFileSystemInterface>
+FakeProvidedFileSystem::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
} // namespace file_system_provider
} // namespace chromeos
diff --git a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h
index 3401dc0..18feb7b 100644
--- a/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h
+++ b/chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h
@@ -5,8 +5,9 @@
#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FAKE_PROVIDED_FILE_SYSTEM_H_
#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FAKE_PROVIDED_FILE_SYSTEM_H_
-#include <set>
+#include <map>
+#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
@@ -23,6 +24,11 @@ namespace file_system_provider {
class RequestManager;
+extern const char kFakeFileName[];
+extern const char kFakeFilePath[];
+extern const char kFakeFileText[];
+extern const size_t kFakeFileSize;
+
// Fake provided file system implementation. Does not communicate with target
// extensions. Used for unit tests.
class FakeProvidedFileSystem : public ProvidedFileSystemInterface {
@@ -54,6 +60,7 @@ class FakeProvidedFileSystem : public ProvidedFileSystemInterface {
const ReadChunkReceivedCallback& callback) OVERRIDE;
virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const OVERRIDE;
virtual RequestManager* GetRequestManager() OVERRIDE;
+ virtual base::WeakPtr<ProvidedFileSystemInterface> GetWeakPtr() OVERRIDE;
// Factory callback, to be used in Service::SetFileSystemFactory(). The
// |event_router| argument can be NULL.
@@ -62,10 +69,13 @@ class FakeProvidedFileSystem : public ProvidedFileSystemInterface {
const ProvidedFileSystemInfo& file_system_info);
private:
+ typedef std::map<int, base::FilePath> OpenedFilesMap;
+
ProvidedFileSystemInfo file_system_info_;
- std::set<int> opened_files_;
+ OpenedFilesMap opened_files_;
int last_file_handle_;
+ base::WeakPtrFactory<ProvidedFileSystemInterface> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(FakeProvidedFileSystem);
};
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc b/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc
index 2b41fe3..c63e8a2 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h"
#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
#include "content/public/browser/browser_thread.h"
#include "webkit/browser/blob/file_stream_reader.h"
@@ -36,8 +37,9 @@ BackendDelegate::CreateFileStreamReader(
fileapi::FileSystemContext* context) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_EQ(fileapi::kFileSystemTypeProvided, url.type());
- NOTIMPLEMENTED();
- return scoped_ptr<webkit_blob::FileStreamReader>();
+
+ return scoped_ptr<webkit_blob::FileStreamReader>(
+ new FileStreamReader(context, url, offset, expected_modification_time));
}
scoped_ptr<fileapi::FileStreamWriter> BackendDelegate::CreateFileStreamWriter(
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 0000000..c7a5aec
--- /dev/null
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc
@@ -0,0 +1,330 @@
+// 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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // TODO(mtomasz): Check if the modification time of the file is as expected.
+ util::FileSystemURLParser parser(url);
+ if (!parser.Parse()) {
+ callback.Run(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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ 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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ 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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // If the file system got unmounted, then abort the reading operation.
+ if (!file_system.get()) {
+ callback.Run(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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(chunk_received_callback, chunk_length, has_next, result));
+}
+
+// Requests metadata of a file. In case of either succes or a failure,
+// |callback is executed. Must be called on UI thread.
+void GetMetadataOnUIThread(
+ base::WeakPtr<ProvidedFileSystemInterface> file_system,
+ const base::FilePath& file_path,
+ const fileapi::AsyncFileUtil::GetFileInfoCallback& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // If the file system got unmounted, then abort the get length operation.
+ if (!file_system.get()) {
+ callback.Run(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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ 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)
+ : url_(url),
+ current_offset_(initial_offset),
+ current_length_(0),
+ expected_modification_time_(expected_modification_time),
+ file_handle_(0),
+ weak_ptr_factory_(this) {
+}
+
+FileStreamReader::~FileStreamReader() {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&CloseFileOnUIThread, file_system_, file_handle_));
+}
+
+void FileStreamReader::Initialize(
+ const base::Closure& pending_closure,
+ const net::Int64CompletionCallback& error_callback) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // 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_handle_) {
+ 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 GetLength().
+ if (!file_handle_) {
+ 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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // If the file system got unmounted, then abort the reading operation.
+ if (!file_handle_) {
+ 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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // If the file system got unmounted, then abort the length fetching operation.
+ if (!file_handle_) {
+ 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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ current_length_ += chunk_length;
+
+ // 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));
+ return;
+ }
+
+ // 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) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // 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
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h
new file mode 100644
index 0000000..7c0b91e
--- /dev/null
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h
@@ -0,0 +1,103 @@
+// 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
+#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "webkit/browser/blob/file_stream_reader.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+namespace fileapi {
+class AsyncFileUtil;
+} // namespace fileapi
+
+namespace chromeos {
+namespace file_system_provider {
+
+class ProvidedFileSystemInterface;
+
+// Implements a streamed file reader. It is lazily initialized by the first call
+// to Read().
+class FileStreamReader : public webkit_blob::FileStreamReader {
+ public:
+ typedef base::Callback<
+ void(base::WeakPtr<ProvidedFileSystemInterface> file_system,
+ const base::FilePath& file_path,
+ int file_handle,
+ base::File::Error result)> InitializeCompletedCallback;
+
+ FileStreamReader(fileapi::FileSystemContext* context,
+ const fileapi::FileSystemURL& url,
+ int64 initial_offset,
+ const base::Time& expected_modification_time);
+
+ virtual ~FileStreamReader();
+
+ // 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;
+
+ private:
+ // Initializes the reader by opening the file. When completed with success,
+ // runs the |pending_closure|. Otherwise, calls the |error_callback|.
+ void Initialize(const base::Closure& pending_closure,
+ const net::Int64CompletionCallback& error_callback);
+
+ // Called when initializing is completed with either a success or an error.
+ void 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);
+
+ // Called when a file system provider returns chunk of read data. Note, that
+ // this may be called multiple times per single Read() call, as long as
+ // |has_next| is set to true. |result| is set to success only if reading is
+ // successful, and the file has not changed while reading.
+ void OnReadChunkReceived(const net::CompletionCallback& callback,
+ int chunk_length,
+ bool has_next,
+ base::File::Error result);
+
+ // Called when fetching length of the file is completed with either a success
+ // or an error.
+ void OnGetMetadataForGetLengthReceived(
+ const net::Int64CompletionCallback& callback,
+ base::File::Error result,
+ const base::File::Info& file_info);
+
+ // Same as Read(), but called after initializing is completed.
+ void ReadAfterInitialized(net::IOBuffer* buffer,
+ int buffer_length,
+ const net::CompletionCallback& callback);
+
+ // Same as GetLength(), but called after initializing is completed.
+ void GetLengthAfterInitialized(const net::Int64CompletionCallback& callback);
+
+ fileapi::FileSystemURL url_;
+ int64 current_offset_;
+ int64 current_length_;
+ base::Time expected_modification_time_;
+
+ // Set during initialization (in case of a success).
+ base::WeakPtr<ProvidedFileSystemInterface> file_system_;
+ base::FilePath file_path_;
+ int file_handle_;
+
+ base::WeakPtrFactory<FileStreamReader> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(FileStreamReader);
+};
+
+} // namespace file_system_provider
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc
new file mode 100644
index 0000000..dfd7e67
--- /dev/null
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc
@@ -0,0 +1,294 @@
+// 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 <string>
+#include <vector>
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h"
+#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
+#include "chrome/browser/chromeos/file_system_provider/service.h"
+#include "chrome/browser/chromeos/file_system_provider/service_factory.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_file_system_context.h"
+#include "extensions/browser/extension_registry.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_util.h"
+#include "webkit/browser/fileapi/external_mount_points.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+namespace chromeos {
+namespace file_system_provider {
+namespace {
+
+const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
+
+// Logs callbacks invocations on the file stream reader.
+class EventLogger {
+ public:
+ EventLogger() : weak_ptr_factory_(this) {}
+ virtual ~EventLogger() {}
+
+ void OnRead(int result) { results_.push_back(result); }
+ void OnGetLength(int64 result) { results_.push_back(result); }
+
+ base::WeakPtr<EventLogger> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ const std::vector<int64>& results() const { return results_; }
+
+ private:
+ std::vector<int64> results_;
+ base::WeakPtrFactory<EventLogger> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventLogger);
+};
+
+// Creates a cracked FileSystemURL for tests.
+fileapi::FileSystemURL CreateFileSystemURL(const std::string& mount_point_name,
+ const base::FilePath& file_path) {
+ const std::string origin = std::string("chrome-extension://") + kExtensionId;
+ const fileapi::ExternalMountPoints* const mount_points =
+ fileapi::ExternalMountPoints::GetSystemInstance();
+ return mount_points->CreateCrackedFileSystemURL(
+ GURL(origin),
+ fileapi::kFileSystemTypeExternal,
+ base::FilePath::FromUTF8Unsafe(mount_point_name).Append(file_path));
+}
+
+// Creates a Service instance. Used to be able to destroy the service in
+// TearDown().
+KeyedService* CreateService(content::BrowserContext* context) {
+ return new Service(Profile::FromBrowserContext(context),
+ extensions::ExtensionRegistry::Get(context));
+}
+
+} // namespace
+
+class FileSystemProviderFileStreamReader : public testing::Test {
+ protected:
+ FileSystemProviderFileStreamReader() {}
+ virtual ~FileSystemProviderFileStreamReader() {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ profile_manager_.reset(
+ new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+ ASSERT_TRUE(profile_manager_->SetUp());
+ profile_ = profile_manager_->CreateTestingProfile("testing-profile");
+
+ ServiceFactory::GetInstance()->SetTestingFactory(profile_, &CreateService);
+ Service* service = Service::Get(profile_); // Owned by its factory.
+ service->SetFileSystemFactoryForTests(
+ base::Bind(&FakeProvidedFileSystem::Create));
+
+ const int file_system_id =
+ service->MountFileSystem(kExtensionId, "testing-file-system");
+ ASSERT_LT(0, file_system_id);
+ const ProvidedFileSystemInfo& file_system_info =
+ service->GetProvidedFileSystem(kExtensionId, file_system_id)
+ ->GetFileSystemInfo();
+ const std::string mount_point_name =
+ file_system_info.mount_path().BaseName().AsUTF8Unsafe();
+
+ file_url_ = CreateFileSystemURL(
+ mount_point_name, base::FilePath::FromUTF8Unsafe(kFakeFilePath + 1));
+ ASSERT_TRUE(file_url_.is_valid());
+ wrong_file_url_ = CreateFileSystemURL(
+ mount_point_name, base::FilePath::FromUTF8Unsafe("im-not-here.txt"));
+ ASSERT_TRUE(wrong_file_url_.is_valid());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Setting the testing factory to NULL will destroy the created service
+ // associated with the testing profile.
+ ServiceFactory::GetInstance()->SetTestingFactory(profile_, NULL);
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ base::ScopedTempDir data_dir_;
+ scoped_ptr<TestingProfileManager> profile_manager_;
+ TestingProfile* profile_; // Owned by TestingProfileManager.
+ fileapi::FileSystemURL file_url_;
+ fileapi::FileSystemURL wrong_file_url_;
+};
+
+TEST_F(FileSystemProviderFileStreamReader, Read_AllAtOnce) {
+ EventLogger logger;
+
+ const int64 initial_offset = 0;
+ FileStreamReader reader(NULL,
+ file_url_,
+ initial_offset,
+ base::Time::Now()); // Not used yet.
+ scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(kFakeFileSize));
+
+ const int result =
+ reader.Read(io_buffer.get(),
+ kFakeFileSize,
+ base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(1u, logger.results().size());
+ EXPECT_LT(0, logger.results()[0]);
+ EXPECT_EQ(kFakeFileSize, static_cast<size_t>(logger.results()[0]));
+
+ std::string buffer_as_string(io_buffer->data(), kFakeFileSize);
+ EXPECT_EQ(kFakeFileText, buffer_as_string);
+}
+
+TEST_F(FileSystemProviderFileStreamReader, Read_WrongFile) {
+ EventLogger logger;
+
+ const int64 initial_offset = 0;
+ FileStreamReader reader(NULL,
+ wrong_file_url_,
+ initial_offset,
+ base::Time::Now()); // Not used yet.
+ scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(kFakeFileSize));
+
+ const int result =
+ reader.Read(io_buffer.get(),
+ kFakeFileSize,
+ base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(1u, logger.results().size());
+ EXPECT_EQ(net::ERR_FAILED, logger.results()[0]);
+}
+
+TEST_F(FileSystemProviderFileStreamReader, Read_InChunks) {
+ EventLogger logger;
+
+ const int64 initial_offset = 0;
+ FileStreamReader reader(NULL,
+ file_url_,
+ initial_offset,
+ base::Time::Now()); // Not used yet.
+
+ for (size_t offset = 0; offset < kFakeFileSize; ++offset) {
+ scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(1));
+ const int result =
+ reader.Read(io_buffer.get(),
+ 1,
+ base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(offset + 1u, logger.results().size());
+ EXPECT_EQ(1, logger.results()[offset]);
+ EXPECT_EQ(kFakeFileText[offset], io_buffer->data()[0]);
+ }
+}
+
+TEST_F(FileSystemProviderFileStreamReader, Read_Slice) {
+ EventLogger logger;
+
+ // Trim first 3 and last 3 characters.
+ const int64 initial_offset = 3;
+ const int length = static_cast<int>(kFakeFileSize) - initial_offset - 3;
+ ASSERT_GT(kFakeFileSize, static_cast<size_t>(initial_offset));
+ ASSERT_LT(0, length);
+
+ FileStreamReader reader(NULL,
+ file_url_,
+ initial_offset,
+ base::Time::Now()); // Not used yet.
+ scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(length));
+
+ const int result =
+ reader.Read(io_buffer.get(),
+ length,
+ base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(1u, logger.results().size());
+ EXPECT_EQ(length, logger.results()[0]);
+
+ std::string buffer_as_string(io_buffer->data(), length);
+ std::string expected_buffer(kFakeFileText + initial_offset, length);
+ EXPECT_EQ(expected_buffer, buffer_as_string);
+}
+
+TEST_F(FileSystemProviderFileStreamReader, Read_Beyond) {
+ EventLogger logger;
+
+ // Request reading 1KB more than available.
+ const int64 initial_offset = 0;
+ const int length = static_cast<int>(kFakeFileSize) + 1024;
+
+ FileStreamReader reader(NULL,
+ file_url_,
+ initial_offset,
+ base::Time::Now()); // Not used yet.
+ scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(length));
+
+ const int result =
+ reader.Read(io_buffer.get(),
+ length,
+ base::Bind(&EventLogger::OnRead, logger.GetWeakPtr()));
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(1u, logger.results().size());
+ EXPECT_LT(0, logger.results()[0]);
+ EXPECT_EQ(kFakeFileSize, static_cast<size_t>(logger.results()[0]));
+
+ std::string buffer_as_string(io_buffer->data(), kFakeFileSize);
+ EXPECT_EQ(kFakeFileText, buffer_as_string);
+}
+
+TEST_F(FileSystemProviderFileStreamReader, GetLength) {
+ EventLogger logger;
+
+ const int64 initial_offset = 0;
+ FileStreamReader reader(NULL,
+ file_url_,
+ initial_offset,
+ base::Time::Now()); // Not used yet.
+
+ const int result = reader.GetLength(
+ base::Bind(&EventLogger::OnGetLength, logger.GetWeakPtr()));
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(1u, logger.results().size());
+ EXPECT_LT(0, logger.results()[0]);
+ EXPECT_EQ(kFakeFileSize, static_cast<size_t>(logger.results()[0]));
+}
+
+TEST_F(FileSystemProviderFileStreamReader, GetLength_WrongFile) {
+ EventLogger logger;
+
+ const int64 initial_offset = 0;
+ FileStreamReader reader(NULL,
+ wrong_file_url_,
+ initial_offset,
+ base::Time::Now()); // Not used yet.
+
+ const int result = reader.GetLength(
+ base::Bind(&EventLogger::OnGetLength, logger.GetWeakPtr()));
+ EXPECT_EQ(net::ERR_IO_PENDING, result);
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(1u, logger.results().size());
+ EXPECT_EQ(net::ERR_FAILED, logger.results()[0]);
+}
+
+} // namespace file_system_provider
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_file.cc b/chrome/browser/chromeos/file_system_provider/operations/read_file.cc
index 55ba7b0..7dafd4c 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_file.cc
+++ b/chrome/browser/chromeos/file_system_provider/operations/read_file.cc
@@ -54,7 +54,7 @@ ReadFile::ReadFile(
buffer_(buffer),
offset_(offset),
length_(length),
- current_offset_(offset),
+ current_offset_(0),
callback_(callback) {
}
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc b/chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc
index 3f39e4a..01108cf 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc
@@ -6,10 +6,10 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
-#include "base/json/json_reader.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/values.h"
#include "chrome/browser/chromeos/file_system_provider/operations/read_file.h"
#include "chrome/common/extensions/api/file_system_provider.h"
#include "chrome/common/extensions/api/file_system_provider_internal.h"
@@ -181,7 +181,6 @@ TEST_F(FileSystemProviderOperationsReadFileTest, Execute_NoListener) {
}
TEST_F(FileSystemProviderOperationsReadFileTest, OnSuccess) {
- using extensions::api::file_system_provider::EntryMetadata;
using extensions::api::file_system_provider_internal::
ReadFileRequestedSuccess::Params;
@@ -202,44 +201,33 @@ TEST_F(FileSystemProviderOperationsReadFileTest, OnSuccess) {
EXPECT_TRUE(read_file.Execute(kRequestId));
- // Sample input as JSON. Keep in sync with file_system_provider_api.idl.
- // As for now, it is impossible to create *::Params class directly, not from
- // base::Value.
- const std::string input =
- "[\n"
- " 1,\n" // kFileSystemId
- " 2,\n" // kRequestId
- " \"ABCDE\",\n" // 5 bytes
- " false\n" // has_next
- "]\n";
-
- int json_error_code;
- std::string json_error_msg;
- scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
- input, base::JSON_PARSE_RFC, &json_error_code, &json_error_msg));
- ASSERT_TRUE(value.get()) << json_error_msg;
-
- base::ListValue* value_as_list;
- ASSERT_TRUE(value->GetAsList(&value_as_list));
- scoped_ptr<Params> params(Params::Create(*value_as_list));
+ const std::string data = "ABCDE";
+ const bool has_next = false;
+
+ base::ListValue value_as_list;
+ value_as_list.Set(0, new base::FundamentalValue(kFileSystemId));
+ value_as_list.Set(1, new base::FundamentalValue(kRequestId));
+ value_as_list.Set(
+ 2, base::BinaryValue::CreateWithCopiedBuffer(data.c_str(), data.size()));
+ value_as_list.Set(3, new base::FundamentalValue(has_next));
+
+ scoped_ptr<Params> params(Params::Create(value_as_list));
ASSERT_TRUE(params.get());
scoped_ptr<RequestValue> request_value(
RequestValue::CreateForReadFileSuccess(params.Pass()));
ASSERT_TRUE(request_value.get());
- const bool has_next = false;
read_file.OnSuccess(kRequestId, request_value.Pass(), has_next);
ASSERT_EQ(1u, callback_logger.events().size());
CallbackLogger::Event* event = callback_logger.events()[0];
EXPECT_EQ(kLength, event->chunk_length());
EXPECT_FALSE(event->has_next());
- EXPECT_EQ("ABCDE", std::string(io_buffer_->data() + kOffset, kLength));
+ EXPECT_EQ(data, std::string(io_buffer_->data(), kLength));
EXPECT_EQ(base::File::FILE_OK, event->result());
}
TEST_F(FileSystemProviderOperationsReadFileTest, OnError) {
- using extensions::api::file_system_provider::EntryMetadata;
using extensions::api::file_system_provider_internal::ReadFileRequestedError::
Params;
diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system.cc b/chrome/browser/chromeos/file_system_provider/provided_file_system.cc
index b619e4e..406619f 100644
--- a/chrome/browser/chromeos/file_system_provider/provided_file_system.cc
+++ b/chrome/browser/chromeos/file_system_provider/provided_file_system.cc
@@ -15,13 +15,19 @@
#include "chrome/common/extensions/api/file_system_provider.h"
#include "extensions/browser/event_router.h"
+namespace net {
+class IOBuffer;
+} // namespace net
+
namespace chromeos {
namespace file_system_provider {
ProvidedFileSystem::ProvidedFileSystem(
extensions::EventRouter* event_router,
const ProvidedFileSystemInfo& file_system_info)
- : event_router_(event_router), file_system_info_(file_system_info) {
+ : event_router_(event_router),
+ file_system_info_(file_system_info),
+ weak_ptr_factory_(this) {
}
ProvidedFileSystem::~ProvidedFileSystem() {}
@@ -120,5 +126,9 @@ RequestManager* ProvidedFileSystem::GetRequestManager() {
return &request_manager_;
}
+base::WeakPtr<ProvidedFileSystemInterface> ProvidedFileSystem::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
} // namespace file_system_provider
} // namespace chromeos
diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system.h b/chrome/browser/chromeos/file_system_provider/provided_file_system.h
index dffc73e..91606ae 100644
--- a/chrome/browser/chromeos/file_system_provider/provided_file_system.h
+++ b/chrome/browser/chromeos/file_system_provider/provided_file_system.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_PROVIDED_FILE_SYSTEM_H_
#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_PROVIDED_FILE_SYSTEM_H_
+#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
#include "chrome/browser/chromeos/file_system_provider/request_manager.h"
@@ -56,12 +57,14 @@ class ProvidedFileSystem : public ProvidedFileSystemInterface {
const ReadChunkReceivedCallback& callback) OVERRIDE;
virtual const ProvidedFileSystemInfo& GetFileSystemInfo() const OVERRIDE;
virtual RequestManager* GetRequestManager() OVERRIDE;
+ virtual base::WeakPtr<ProvidedFileSystemInterface> GetWeakPtr() OVERRIDE;
private:
extensions::EventRouter* event_router_;
RequestManager request_manager_;
ProvidedFileSystemInfo file_system_info_;
+ base::WeakPtrFactory<ProvidedFileSystemInterface> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ProvidedFileSystem);
};
diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system_info.cc b/chrome/browser/chromeos/file_system_provider/provided_file_system_info.cc
index b552436..ff98eda 100644
--- a/chrome/browser/chromeos/file_system_provider/provided_file_system_info.cc
+++ b/chrome/browser/chromeos/file_system_provider/provided_file_system_info.cc
@@ -2,7 +2,7 @@
// 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/provided_file_system.h"
+#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
namespace chromeos {
namespace file_system_provider {
diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h b/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h
index b78b2a4..b228e67 100644
--- a/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h
+++ b/chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
#include "webkit/browser/fileapi/async_file_util.h"
class EventRouter;
@@ -84,6 +85,9 @@ class ProvidedFileSystemInterface {
// Returns a request manager for the file system.
virtual RequestManager* GetRequestManager() = 0;
+
+ // Returns a weak pointer to this object.
+ virtual base::WeakPtr<ProvidedFileSystemInterface> GetWeakPtr() = 0;
};
} // namespace file_system_provider
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index a040fc7..04fd83f 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -277,7 +277,8 @@ fileapi::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation(
bool FileSystemBackend::SupportsStreaming(
const fileapi::FileSystemURL& url) const {
- return url.type() == fileapi::kFileSystemTypeDrive;
+ return url.type() == fileapi::kFileSystemTypeDrive ||
+ url.type() == fileapi::kFileSystemTypeProvided;
}
scoped_ptr<webkit_blob::FileStreamReader>
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 6e41a27..49aa3fc 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -376,6 +376,8 @@
'browser/chromeos/file_manager/zip_file_creator.h',
'browser/chromeos/file_system_provider/fileapi/backend_delegate.cc',
'browser/chromeos/file_system_provider/fileapi/backend_delegate.h',
+ 'browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc',
+ 'browser/chromeos/file_system_provider/fileapi/file_stream_reader.h',
'browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc',
'browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h',
'browser/chromeos/file_system_provider/mount_path_util.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index a97c1b1..dcceebb 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -708,6 +708,7 @@
'browser/chromeos/file_manager/volume_manager_unittest.cc',
'browser/chromeos/file_system_provider/fake_provided_file_system.cc',
'browser/chromeos/file_system_provider/fake_provided_file_system.h',
+ 'browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc',
'browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc',
'browser/chromeos/file_system_provider/mount_path_util_unittest.cc',
'browser/chromeos/file_system_provider/operations/close_file_unittest.cc',
diff --git a/chrome/common/extensions/api/file_system_provider.idl b/chrome/common/extensions/api/file_system_provider.idl
index 8b02cff..bee9903 100644
--- a/chrome/common/extensions/api/file_system_provider.idl
+++ b/chrome/common/extensions/api/file_system_provider.idl
@@ -85,7 +85,7 @@ namespace fileSystemProvider {
// data will be returned, then <code>hasNext</code> must be true, and it
// has to be called again with additional entries. If no more data is
// available, then <code>hasNext</code> must be set to false.
- callback FileDataCallback = void(DOMString data, bool hasNext);
+ callback FileDataCallback = void(ArrayBuffer data, bool hasNext);
interface Functions {
// Mounts a file system with the given <code>displayName</code>.
@@ -145,6 +145,7 @@ namespace fileSystemProvider {
// exist, then it should be created.
[maxListeners=1] static void onOpenFileRequested(
long fileSystemId,
+ long requestId,
DOMString filePath,
OpenFileMode mode,
boolean create,
diff --git a/chrome/common/extensions/api/file_system_provider_internal.idl b/chrome/common/extensions/api/file_system_provider_internal.idl
index f63cc0e..818c3fb 100644
--- a/chrome/common/extensions/api/file_system_provider_internal.idl
+++ b/chrome/common/extensions/api/file_system_provider_internal.idl
@@ -80,7 +80,7 @@ namespace fileSystemProviderInternal {
static void readFileRequestedSuccess(
long fileSystemId,
long requestId,
- DOMString data,
+ ArrayBuffer data,
boolean hasNext);
// Internal. Error callback of the <code>onReadFileRequested</code>
diff --git a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
index cb06cda..9f7384f 100644
--- a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
@@ -195,8 +195,8 @@ eventBindings.registerArgumentMassager(
fileSystemProviderInternal.closeFileRequestedError(
fileSystemId, requestId, error);
}
- dispatch([fileSystemId, openRequestId, openRequestId, onSuccessCallback,
- onErrorCallback]);
+ dispatch([fileSystemId, openRequestId, onSuccessCallback,
+ onErrorCallback]);
});
eventBindings.registerArgumentMassager(
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json
new file mode 100644
index 0000000..339c3cf
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/read_file/manifest.json
@@ -0,0 +1,18 @@
+{
+ "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDOuXEIuoK1kAkBe0SKiJn/N9oNn3oUxGa4dwj40MnJqPn+w0aR2vuyocm0R4Drp67aYwtLjOVPF4CICRq6ICP6eU07gGwQxGdZ7HJASXV8hm0tab5I70oJmRLfFJyVAMCeWlFaOGq05v2i6EbifZM0qO5xALKNGQt+yjXi5INM5wIBIw==",
+ "name": "chrome.fileSystemProvider.onReadFileRequested",
+ "version": "0.1",
+ "manifest_version": 2,
+ "description":
+ "Test for chrome.fileSystemProvider.onReadFileRequested().",
+ "permissions": [
+ "fileSystemProvider",
+ "fileBrowserPrivate",
+ "fileBrowserHandler"
+ ],
+ "app": {
+ "background": {
+ "scripts": ["test.js"]
+ }
+ }
+}
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/read_file/test.js b/chrome/test/data/extensions/api_test/file_system_provider/read_file/test.js
new file mode 100644
index 0000000..b9d7b08
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/read_file/test.js
@@ -0,0 +1,292 @@
+// 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.
+
+'use strict';
+
+/**
+ * @type {number}
+ */
+var fileSystemId = 0;
+
+/**
+ * @type {?DOMFileSystem}
+ */
+var fileSystem = null;
+
+/**
+ * Map of opened files, from a <code>openRequestId</code> to <code>filePath
+ * </code>.
+ * @type {Object.<number, string>}
+ */
+var openedFiles = {};
+
+/**
+ * @type {Object}
+ * @const
+ */
+var TESTING_ROOT = Object.freeze({
+ isDirectory: true,
+ name: '',
+ size: 0,
+ modificationTime: new Date(2014, 4, 28, 10, 39, 15)
+});
+
+/**
+ * Testing contents for files.
+ * @type {string}
+ * @const
+ */
+var TESTING_TEXT = 'I have a basket full of fruits.';
+
+/**
+ * Metadata of a healthy file used to read contents from.
+ * @type {Object}
+ * @const
+ */
+var TESTING_TIRAMISU_FILE = Object.freeze({
+ isDirectory: false,
+ name: 'tiramisu.txt',
+ size: TESTING_TEXT.length,
+ modificationTime: new Date(2014, 1, 25, 7, 36, 12)
+});
+
+/**
+ * Metadata of a broken file used to read contents from.
+ * @type {Object}
+ * @const
+ */
+var TESTING_BROKEN_TIRAMISU_FILE = Object.freeze({
+ isDirectory: false,
+ name: 'broken-tiramisu.txt',
+ size: TESTING_TEXT.length,
+ modificationTime: new Date(2014, 1, 25, 7, 36, 12)
+});
+
+/**
+ * Returns metadata for the requested entry.
+ *
+ * To successfully acquire a DirectoryEntry, or even a DOMFileSystem, this event
+ * must be implemented and return correct values.
+ *
+ * @param {number} inFileSystemId ID of the file system.
+ * @param {string} entryPath Path of the requested entry.
+ * @param {function(Object)} onSuccess Success callback with metadata passed
+ * an argument.
+ * @param {function(string)} onError Error callback with an error code.
+ */
+function onGetMetadataRequested(
+ inFileSystemId, entryPath, onSuccess, onError) {
+ if (inFileSystemId != fileSystemId) {
+ onError('SECURITY_ERROR'); // enum ProviderError.
+ return;
+ }
+
+ if (entryPath == '/') {
+ onSuccess(TESTING_ROOT);
+ return;
+ }
+
+ if (entryPath == '/' + TESTING_TIRAMISU_FILE.name) {
+ onSuccess(TESTING_TIRAMISU_FILE);
+ return;
+ }
+
+ if (entryPath == '/' + TESTING_BROKEN_TIRAMISU_FILE.name) {
+ onSuccess(TESTING_BROKEN_TIRAMISU_FILE);
+ return;
+ }
+
+ onError('NOT_FOUND'); // enum ProviderError.
+}
+
+/**
+ * Requests opening a file at <code>filePath</code>. Further file operations
+ * will be associated with the <code>requestId</code>
+ *
+ * @param {number} inFileSystemId ID of the file system.
+ * @param {number} requestId ID of the opening request. Used later for reading.
+ * @param {string} filePath Path of the file to be opened.
+ * @param {string} mode Mode, either reading or writing.
+ * @param {boolean} create True to create if doesn't exist.
+ * @param {function()} onSuccess Success callback.
+ * @param {function(string)} onError Error callback.
+ */
+function onOpenFileRequested(
+ inFileSystemId, requestId, filePath, mode, create, onSuccess, onError) {
+ if (inFileSystemId != fileSystemId || mode != 'READ' || create) {
+ onError('SECURITY_ERROR'); // enum ProviderError.
+ return;
+ }
+
+ if (filePath == '/' + TESTING_TIRAMISU_FILE.name ||
+ filePath == '/' + TESTING_BROKEN_TIRAMISU_FILE.name) {
+ openedFiles[requestId] = filePath;
+ onSuccess();
+ } else {
+ onError('NOT_FOUND'); // enum ProviderError.
+ }
+}
+
+/**
+ * Requests closing a file previously opened with <code>openRequestId</code>.
+ *
+ * @param {number} inFileSystemId ID of the file system.
+ * @param {number} openRequestId ID of the request used to open the file.
+ * @param {function()} onSuccess Success callback.
+ * @param {function(string)} onError Error callback.
+ */
+function onCloseFileRequested(
+ inFileSystemId, openRequestId, onSuccess, onError) {
+ if (inFileSystemId != fileSystemId || !openedFiles[openRequestId]) {
+ onError('SECURITY_ERROR'); // enum ProviderError.
+ return;
+ }
+
+ delete openedFiles[requestId];
+ onSuccess();
+}
+
+/**
+ * Requests reading contents of a file, previously opened with <code>
+ * openRequestId</code>.
+ *
+ * @param {number} inFileSystemId ID of the file system.
+ * @param {number} openRequestId ID of the request used to open the file.
+ * @param {number} offset Offset of the file.
+ * @param {number} length Number of bytes to read.
+ * @param {function(ArrayBuffer, boolean)} onSuccess Success callback with a
+ * chunk of data, and information if more data will be provided later.
+ * @param {function(string)} onError Error callback.
+ */
+function onReadFileRequested(
+ inFileSystemId, openRequestId, offset, length, onSuccess, onError) {
+ var filePath = openedFiles[openRequestId];
+ if (inFileSystemId != fileSystemId || !filePath) {
+ onError('SECURITY_ERROR'); // enum ProviderError.
+ return;
+ }
+
+ if (filePath == '/' + TESTING_TIRAMISU_FILE.name) {
+ var textToSend = TESTING_TEXT.substr(offset, length);
+ var textToSendInChunks = textToSend.split(/(?= )/);
+
+ textToSendInChunks.forEach(function(item, index) {
+ // Convert item (string) to an ArrayBuffer.
+ var reader = new FileReader();
+
+ reader.onload = function(e) {
+ onSuccess(
+ e.target.result,
+ index < textToSendInChunks.length - 1 /* has_next */);
+ };
+
+ reader.readAsArrayBuffer(new Blob([item]));
+ });
+ return;
+ }
+
+ if (filePath == '/' + TESTING_BROKEN_TIRAMISU_FILE.name) {
+ onError('ACCESS_DENIED'); // enum ProviderError.
+ return;
+ }
+
+ onError('INVALID_OPERATION'); // enum ProviderError.
+}
+
+/**
+ * Sets up the tests. Called once per all test cases. In case of a failure,
+ * the callback is not called.
+ *
+ * @param {function()} callback Success callback.
+ */
+function setUp(callback) {
+ chrome.fileSystemProvider.mount('chocolate.zip', function(id) {
+ fileSystemId = id;
+ chrome.fileSystemProvider.onGetMetadataRequested.addListener(
+ onGetMetadataRequested);
+ chrome.fileSystemProvider.onOpenFileRequested.addListener(
+ onOpenFileRequested);
+ chrome.fileSystemProvider.onReadFileRequested.addListener(
+ onReadFileRequested);
+ var volumeId =
+ 'provided:' + chrome.runtime.id + '-' + fileSystemId + '-user';
+
+ chrome.fileBrowserPrivate.requestFileSystem(
+ volumeId,
+ function(inFileSystem) {
+ chrome.test.assertTrue(!!inFileSystem);
+
+ fileSystem = inFileSystem;
+ callback();
+ });
+ }, function() {
+ chrome.test.fail();
+ });
+}
+
+/**
+ * Runs all of the test cases, one by one.
+ */
+function runTests() {
+ chrome.test.runTests([
+ // Read contents of the /tiramisu.txt file. This file exists, so it should
+ // succeed.
+ function readFileSuccess() {
+ var onTestSuccess = chrome.test.callbackPass();
+ fileSystem.root.getFile(
+ TESTING_TIRAMISU_FILE.name,
+ {create: false},
+ function(fileEntry) {
+ fileEntry.file(function(file) {
+ var fileReader = new FileReader();
+ fileReader.onload = function(e) {
+ var text = fileReader.result;
+ chrome.test.assertEq(TESTING_TEXT, text);
+ onTestSuccess();
+ };
+ fileReader.onerror = function(e) {
+ chrome.test.fail(fileReader.error.name);
+ };
+ fileReader.readAsText(file);
+ },
+ function(error) {
+ chrome.test.fail(error.name);
+ });
+ },
+ function(error) {
+ chrome.test.fail(error.name);
+ });
+ },
+ // Read contents of a file file, but with an error on the way. This should
+ // result in an error.
+ function readEntriesError() {
+ var onTestSuccess = chrome.test.callbackPass();
+ fileSystem.root.getFile(
+ TESTING_BROKEN_TIRAMISU_FILE.name,
+ {create: false},
+ function(fileEntry) {
+ fileEntry.file(function(file) {
+ var fileReader = new FileReader();
+ fileReader.onload = function(e) {
+ chrome.test.fail();
+ };
+ fileReader.onerror = function(e) {
+ chrome.test.assertEq('NotReadableError', fileReader.error.name);
+ onTestSuccess();
+ };
+ fileReader.readAsText(file);
+ },
+ function(error) {
+ chrome.test.fail();
+ });
+ },
+ function(error) {
+ chrome.test.fail(error.name);
+ });
+ }
+ ]);
+}
+
+// Setup and run all of the test cases.
+setUp(runTests);