diff options
author | sammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-29 14:16:24 +0000 |
---|---|---|
committer | sammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-29 14:16:24 +0000 |
commit | 6b7ecdd48738d917c870a18e1ba3bb7707a4894e (patch) | |
tree | a1fd71148f5c1f89c166ac35cce308c29ddcb5a1 /chrome/browser/extensions/api/file_system | |
parent | f5f2b724da0926b45c9a6bd34df0e974206819c5 (diff) | |
download | chromium_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')
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 |