summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/api/file_system
diff options
context:
space:
mode:
authorsammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-29 14:16:24 +0000
committersammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-29 14:16:24 +0000
commit6b7ecdd48738d917c870a18e1ba3bb7707a4894e (patch)
treea1fd71148f5c1f89c166ac35cce308c29ddcb5a1 /chrome/browser/extensions/api/file_system
parentf5f2b724da0926b45c9a6bd34df0e974206819c5 (diff)
downloadchromium_src-6b7ecdd48738d917c870a18e1ba3bb7707a4894e.zip
chromium_src-6b7ecdd48738d917c870a18e1ba3bb7707a4894e.tar.gz
chromium_src-6b7ecdd48738d917c870a18e1ba3bb7707a4894e.tar.bz2
Add support for directory access to the file system API.
This adds 'openDirectory' as a type option for chrome.fileSystem.chooseEntry to allow access to directories. This is restricted to apps with the fileSystem.directory permission. BUG=148486 Review URL: https://chromiumcodereview.appspot.com/23146016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220287 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/api/file_system')
-rw-r--r--chrome/browser/extensions/api/file_system/file_system_api.cc116
-rw-r--r--chrome/browser/extensions/api/file_system/file_system_api.h22
-rw-r--r--chrome/browser/extensions/api/file_system/file_system_apitest.cc103
3 files changed, 206 insertions, 35 deletions
diff --git a/chrome/browser/extensions/api/file_system/file_system_api.cc b/chrome/browser/extensions/api/file_system/file_system_api.cc
index 43c69a1..4e3de96 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_api.cc
@@ -60,6 +60,8 @@ const char kUserCancelled[] = "User cancelled";
const char kWritableFileErrorFormat[] = "Error opening %s";
const char kRequiresFileSystemWriteError[] =
"Operation requires fileSystem.write permission";
+const char kRequiresFileSystemDirectoryError[] =
+ "Operation requires fileSystem.directory permission";
const char kMultipleUnsupportedError[] =
"acceptsMultiple: true is not supported for 'saveFile'";
const char kUnknownIdError[] = "Unknown id";
@@ -304,6 +306,7 @@ bool FileSystemGetDisplayPathFunction::RunImpl() {
FileSystemEntryFunction::FileSystemEntryFunction()
: multiple_(false),
+ is_directory_(false),
response_(NULL) {}
void FileSystemEntryFunction::CheckWritableFiles(
@@ -312,6 +315,7 @@ void FileSystemEntryFunction::CheckWritableFiles(
app_file_handler_util::CheckWritableFiles(
paths,
profile_,
+ is_directory_,
base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
this,
paths),
@@ -348,7 +352,8 @@ void FileSystemEntryFunction::AddEntryToResponse(
profile(),
GetExtension(),
render_view_host_->GetProcess()->GetID(),
- path);
+ path,
+ is_directory_);
base::ListValue* entries;
bool success = response_->GetList("entries", &entries);
DCHECK(success);
@@ -360,6 +365,7 @@ void FileSystemEntryFunction::AddEntryToResponse(
entry->SetString("id", file_entry.id);
else
entry->SetString("id", id_override);
+ entry->SetBoolean("isDirectory", is_directory_);
entries->Append(entry);
}
@@ -382,15 +388,39 @@ bool FileSystemGetWritableEntryFunction::RunImpl() {
return false;
}
- base::FilePath path;
if (!ValidateFileEntryAndGetPath(filesystem_name, filesystem_path,
- render_view_host_, &path, &error_))
+ render_view_host_, &path_, &error_))
return false;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(
+ &FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread,
+ this),
+ base::Bind(
+ &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse,
+ this));
+ return true;
+}
+
+void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ if (is_directory_ &&
+ !extension_->HasAPIPermission(APIPermission::kFileSystemDirectory)) {
+ error_ = kRequiresFileSystemDirectoryError;
+ SendResponse(false);
+ }
std::vector<base::FilePath> paths;
- paths.push_back(path);
+ paths.push_back(path_);
CheckWritableFiles(paths);
- return true;
+}
+
+void FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+ if (base::DirectoryExists(path_)) {
+ is_directory_ = true;
+ }
}
bool FileSystemIsWritableEntryFunction::RunImpl() {
@@ -627,8 +657,15 @@ void FileSystemChooseEntryFunction::SetInitialPathOnFileThread(
void FileSystemChooseEntryFunction::FilesSelected(
const std::vector<base::FilePath>& paths) {
DCHECK(!paths.empty());
- file_system_api::SetLastChooseEntryDirectory(
- ExtensionPrefs::Get(profile()), GetExtension()->id(), paths[0].DirName());
+ base::FilePath last_choose_directory;
+ if (is_directory_) {
+ last_choose_directory = paths[0];
+ } else {
+ last_choose_directory = paths[0].DirName();
+ }
+ file_system_api::SetLastChooseEntryDirectory(ExtensionPrefs::Get(profile()),
+ GetExtension()->id(),
+ last_choose_directory);
if (app_file_handler_util::HasFileSystemWritePermission(extension_)) {
CheckWritableFiles(paths);
return;
@@ -732,6 +769,17 @@ bool FileSystemChooseEntryFunction::RunImpl() {
return false;
}
picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
+ } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY) {
+ is_directory_ = true;
+ if (!extension_->HasAPIPermission(APIPermission::kFileSystemDirectory)) {
+ error_ = kRequiresFileSystemDirectoryError;
+ return false;
+ }
+ if (multiple_) {
+ error_ = kMultipleUnsupportedError;
+ return false;
+ }
+ picker_type = ui::SelectFileDialog::SELECT_FOLDER;
}
base::FilePath::StringType suggested_extension;
@@ -767,32 +815,47 @@ bool FileSystemRetainEntryFunction::RunImpl() {
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
SavedFilesService* saved_files_service = SavedFilesService::Get(profile());
// Add the file to the retain list if it is not already on there.
- if (!saved_files_service->IsRegistered(extension_->id(), entry_id) &&
- !RetainFileEntry(entry_id)) {
- return false;
+ if (!saved_files_service->IsRegistered(extension_->id(), entry_id)) {
+ std::string filesystem_name;
+ std::string filesystem_path;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
+ if (!ValidateFileEntryAndGetPath(filesystem_name,
+ filesystem_path,
+ render_view_host_,
+ &path_,
+ &error_)) {
+ return false;
+ }
+
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&FileSystemRetainEntryFunction::SetIsDirectoryOnFileThread,
+ this),
+ base::Bind(
+ &FileSystemRetainEntryFunction::RetainFileEntry, this, entry_id));
+ return true;
}
+
saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
+ SendResponse(true);
return true;
}
-bool FileSystemRetainEntryFunction::RetainFileEntry(
+void FileSystemRetainEntryFunction::RetainFileEntry(
const std::string& entry_id) {
- std::string filesystem_name;
- std::string filesystem_path;
- EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
- EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
- base::FilePath path;
- if (!ValidateFileEntryAndGetPath(filesystem_name,
- filesystem_path,
- render_view_host_,
- &path,
- &error_)) {
- return false;
- }
+ SavedFilesService* saved_files_service = SavedFilesService::Get(profile());
+ saved_files_service->RegisterFileEntry(
+ extension_->id(), entry_id, path_, is_directory_);
+ saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
+ SendResponse(true);
+}
- SavedFilesService::Get(profile())->RegisterFileEntry(
- extension_->id(), entry_id, path);
- return true;
+void FileSystemRetainEntryFunction::SetIsDirectoryOnFileThread() {
+ if (base::DirectoryExists(path_)) {
+ is_directory_ = true;
+ }
}
bool FileSystemIsRestorableFunction::RunImpl() {
@@ -822,6 +885,7 @@ bool FileSystemRestoreEntryFunction::RunImpl() {
// |needs_new_entry| will be false if the renderer already has an Entry for
// |entry_id|.
if (needs_new_entry) {
+ is_directory_ = file_entry->is_directory;
CreateResponse();
AddEntryToResponse(file_entry->path, file_entry->id);
}
diff --git a/chrome/browser/extensions/api/file_system/file_system_api.h b/chrome/browser/extensions/api/file_system/file_system_api.h
index f44d8c6..2a65fa5 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api.h
+++ b/chrome/browser/extensions/api/file_system/file_system_api.h
@@ -74,6 +74,9 @@ class FileSystemEntryFunction : public AsyncExtensionFunction {
// Whether multiple entries have been requested.
bool multiple_;
+ // Whether a directory has been requested.
+ bool is_directory_;
+
// The dictionary to send as the response.
base::DictionaryValue* response_;
};
@@ -86,6 +89,13 @@ class FileSystemGetWritableEntryFunction : public FileSystemEntryFunction {
protected:
virtual ~FileSystemGetWritableEntryFunction() {}
virtual bool RunImpl() OVERRIDE;
+
+ private:
+ void CheckPermissionAndSendResponse();
+ void SetIsDirectoryOnFileThread();
+
+ // The path to the file for which a writable entry has been requested.
+ base::FilePath path_;
};
class FileSystemIsWritableEntryFunction : public SyncExtensionFunction {
@@ -146,7 +156,7 @@ class FileSystemChooseEntryFunction : public FileSystemEntryFunction {
base::FilePath initial_path_;
};
-class FileSystemRetainEntryFunction : public SyncExtensionFunction {
+class FileSystemRetainEntryFunction : public AsyncExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("fileSystem.retainEntry", FILESYSTEM_RETAINENTRY)
@@ -157,7 +167,15 @@ class FileSystemRetainEntryFunction : public SyncExtensionFunction {
private:
// Retains the file entry referenced by |entry_id| in apps::SavedFilesService.
// |entry_id| must refer to an entry in an isolated file system.
- bool RetainFileEntry(const std::string& entry_id);
+ void RetainFileEntry(const std::string& entry_id);
+
+ void SetIsDirectoryOnFileThread();
+
+ // Whether the file being retained is a directory.
+ bool is_directory_;
+
+ // The path to the file to retain.
+ base::FilePath path_;
};
class FileSystemIsRestorableFunction : public SyncExtensionFunction {
diff --git a/chrome/browser/extensions/api/file_system/file_system_apitest.cc b/chrome/browser/extensions/api/file_system/file_system_apitest.cc
index 08570df..b26f530 100644
--- a/chrome/browser/extensions/api/file_system/file_system_apitest.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_apitest.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/extensions/api/file_system/file_system_api.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/features/feature_channel.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_service.h"
@@ -56,9 +57,11 @@ void SetLastChooseEntryDirectoryToAppDirectory(
}
void AddSavedEntry(const base::FilePath& path_to_save,
+ bool is_directory,
apps::SavedFilesService* service,
const Extension* extension) {
- service->RegisterFileEntry(extension->id(), "magic id", path_to_save);
+ service->RegisterFileEntry(
+ extension->id(), "magic id", path_to_save, is_directory);
}
} // namespace
@@ -274,6 +277,58 @@ IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
<< message_;
}
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiOpenDirectoryTest) {
+ ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+ base::FilePath test_file = TempFilePath("open_existing.txt", true);
+ ASSERT_FALSE(test_file.empty());
+ base::FilePath test_directory = test_file.DirName();
+ FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+ &test_directory);
+ ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/open_directory"))
+ << message_;
+ CheckStoredDirectoryMatches(test_file);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+ FileSystemApiOpenDirectoryWithWriteTest) {
+ ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+ base::FilePath test_file = TempFilePath("open_existing.txt", true);
+ ASSERT_FALSE(test_file.empty());
+ base::FilePath test_directory = test_file.DirName();
+ FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+ &test_directory);
+ ASSERT_TRUE(
+ RunPlatformAppTest("api_test/file_system/open_directory_with_write"))
+ << message_;
+ CheckStoredDirectoryMatches(test_file);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+ FileSystemApiOpenDirectoryWithoutPermissionTest) {
+ base::FilePath test_file = TempFilePath("open_existing.txt", true);
+ ASSERT_FALSE(test_file.empty());
+ base::FilePath test_directory = test_file.DirName();
+ FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+ &test_directory);
+ ASSERT_TRUE(RunPlatformAppTest(
+ "api_test/file_system/open_directory_without_permission"))
+ << message_;
+ CheckStoredDirectoryMatches(base::FilePath());
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+ FileSystemApiOpenDirectoryWithOnlyWritePermissionTest) {
+ base::FilePath test_file = TempFilePath("open_existing.txt", true);
+ ASSERT_FALSE(test_file.empty());
+ base::FilePath test_directory = test_file.DirName();
+ FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+ &test_directory);
+ ASSERT_TRUE(RunPlatformAppTest(
+ "api_test/file_system/open_directory_with_only_write"))
+ << message_;
+ CheckStoredDirectoryMatches(base::FilePath());
+}
+
IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
FileSystemApiInvalidChooseEntryTypeTest) {
base::FilePath test_file = TempFilePath("open_existing.txt", true);
@@ -468,17 +523,51 @@ IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRetainEntry) {
EXPECT_EQ(1, file_entries[0].sequence_number);
}
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRetainDirectoryEntry) {
+ ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+ base::FilePath test_file = TempFilePath("open_existing.txt", true);
+ ASSERT_FALSE(test_file.empty());
+ base::FilePath test_directory = test_file.DirName();
+ FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+ &test_directory);
+ ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/retain_directory"))
+ << message_;
+ std::vector<apps::SavedFileEntry> file_entries = apps::SavedFilesService::Get(
+ profile())->GetAllFileEntries(GetSingleLoadedExtension()->id());
+ ASSERT_EQ(1u, file_entries.size());
+ EXPECT_EQ(test_directory, file_entries[0].path);
+ EXPECT_EQ(1, file_entries[0].sequence_number);
+ EXPECT_TRUE(file_entries[0].is_directory);
+}
+
IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRestoreEntry) {
base::FilePath test_file = TempFilePath("writable.txt", true);
ASSERT_FALSE(test_file.empty());
FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
&test_file);
- {
- AppInstallObserver observer(base::Bind(
- AddSavedEntry, test_file, apps::SavedFilesService::Get(profile())));
- ASSERT_TRUE(RunPlatformAppTest(
- "api_test/file_system/restore_entry")) << message_;
- }
+ AppInstallObserver observer(
+ base::Bind(AddSavedEntry,
+ test_file,
+ false,
+ apps::SavedFilesService::Get(profile())));
+ ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/restore_entry"))
+ << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRestoreDirectoryEntry) {
+ ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+ base::FilePath test_file = TempFilePath("writable.txt", true);
+ ASSERT_FALSE(test_file.empty());
+ base::FilePath test_directory = test_file.DirName();
+ FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+ &test_file);
+ AppInstallObserver observer(
+ base::Bind(AddSavedEntry,
+ test_directory,
+ true,
+ apps::SavedFilesService::Get(profile())));
+ ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/restore_directory"))
+ << message_;
}
} // namespace extensions