1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
9 #include "apps/saved_files_service.h"
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/path_service.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/sys_string_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/value_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/extension_util.h"
24 #include "chrome/browser/extensions/path_util.h"
25 #include "chrome/browser/platform_util.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h"
28 #include "chrome/browser/ui/chrome_select_file_policy.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/extensions/api/file_system.h"
31 #include "chrome/grit/generated_resources.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/child_process_security_policy.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "content/public/browser/render_view_host.h"
36 #include "content/public/browser/storage_partition.h"
37 #include "content/public/browser/web_contents.h"
38 #include "extensions/browser/app_window/app_window.h"
39 #include "extensions/browser/app_window/app_window_registry.h"
40 #include "extensions/browser/extension_prefs.h"
41 #include "extensions/browser/extension_system.h"
42 #include "extensions/browser/granted_file_entry.h"
43 #include "extensions/common/permissions/api_permission.h"
44 #include "extensions/common/permissions/permissions_data.h"
45 #include "net/base/mime_util.h"
46 #include "storage/browser/fileapi/external_mount_points.h"
47 #include "storage/browser/fileapi/file_system_operation_runner.h"
48 #include "storage/browser/fileapi/isolated_context.h"
49 #include "storage/common/fileapi/file_system_types.h"
50 #include "storage/common/fileapi/file_system_util.h"
51 #include "ui/base/l10n/l10n_util.h"
52 #include "ui/shell_dialogs/select_file_dialog.h"
53 #include "ui/shell_dialogs/selected_file_info.h"
55 #if defined(OS_MACOSX)
56 #include <CoreFoundation/CoreFoundation.h>
57 #include "base/mac/foundation_util.h"
60 #if defined(OS_CHROMEOS)
61 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
64 using apps::SavedFileEntry;
65 using apps::SavedFilesService;
66 using storage::IsolatedContext;
68 const char kInvalidCallingPage[] = "Invalid calling page. This function can't "
69 "be called from a background page.";
70 const char kUserCancelled[] = "User cancelled";
71 const char kWritableFileErrorFormat[] = "Error opening %s";
72 const char kRequiresFileSystemWriteError[] =
73 "Operation requires fileSystem.write permission";
74 const char kRequiresFileSystemDirectoryError[] =
75 "Operation requires fileSystem.directory permission";
76 const char kMultipleUnsupportedError[] =
77 "acceptsMultiple: true is not supported for 'saveFile'";
78 const char kUnknownIdError[] = "Unknown id";
80 namespace file_system = extensions::api::file_system;
81 namespace ChooseEntry = file_system::ChooseEntry;
85 bool g_skip_picker_for_test = false;
86 bool g_use_suggested_path_for_test = false;
87 base::FilePath* g_path_to_be_picked_for_test;
88 std::vector<base::FilePath>* g_paths_to_be_picked_for_test;
89 bool g_skip_directory_confirmation_for_test = false;
90 bool g_allow_directory_access_for_test = false;
92 // Expand the mime-types and extensions provided in an AcceptOption, returning
93 // them within the passed extension vector. Returns false if no valid types
95 bool GetFileTypesFromAcceptOption(
96 const file_system::AcceptOption& accept_option,
97 std::vector<base::FilePath::StringType>* extensions,
98 base::string16* description) {
99 std::set<base::FilePath::StringType> extension_set;
100 int description_id = 0;
102 if (accept_option.mime_types.get()) {
103 std::vector<std::string>* list = accept_option.mime_types.get();
104 bool valid_type = false;
105 for (std::vector<std::string>::const_iterator iter = list->begin();
106 iter != list->end(); ++iter) {
107 std::vector<base::FilePath::StringType> inner;
108 std::string accept_type = *iter;
109 base::StringToLowerASCII(&accept_type);
110 net::GetExtensionsForMimeType(accept_type, &inner);
115 description_id = 0; // We already have an accept type with label; if
116 // we find another, give up and use the default.
117 else if (accept_type == "image/*")
118 description_id = IDS_IMAGE_FILES;
119 else if (accept_type == "audio/*")
120 description_id = IDS_AUDIO_FILES;
121 else if (accept_type == "video/*")
122 description_id = IDS_VIDEO_FILES;
124 extension_set.insert(inner.begin(), inner.end());
129 if (accept_option.extensions.get()) {
130 std::vector<std::string>* list = accept_option.extensions.get();
131 for (std::vector<std::string>::const_iterator iter = list->begin();
132 iter != list->end(); ++iter) {
133 std::string extension = *iter;
134 base::StringToLowerASCII(&extension);
136 extension_set.insert(base::UTF8ToWide(*iter));
138 extension_set.insert(*iter);
143 extensions->assign(extension_set.begin(), extension_set.end());
144 if (extensions->empty())
147 if (accept_option.description.get())
148 *description = base::UTF8ToUTF16(*accept_option.description.get());
149 else if (description_id)
150 *description = l10n_util::GetStringUTF16(description_id);
155 // Key for the path of the directory of the file last chosen by the user in
156 // response to a chrome.fileSystem.chooseEntry() call.
157 const char kLastChooseEntryDirectory[] = "last_choose_file_directory";
159 const int kGraylistedPaths[] = {
162 base::DIR_PROGRAM_FILES,
163 base::DIR_PROGRAM_FILESX86,
168 typedef base::Callback<void(scoped_ptr<base::File::Info>)> FileInfoOptCallback;
170 // Passes optional file info to the UI thread depending on |result| and |info|.
171 void PassFileInfoToUIThread(const FileInfoOptCallback& callback,
172 base::File::Error result,
173 const base::File::Info& info) {
174 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
175 scoped_ptr<base::File::Info> file_info(
176 result == base::File::FILE_OK ? new base::File::Info(info) : NULL);
177 content::BrowserThread::PostTask(
178 content::BrowserThread::UI,
180 base::Bind(callback, base::Passed(&file_info)));
185 namespace extensions {
187 namespace file_system_api {
189 base::FilePath GetLastChooseEntryDirectory(const ExtensionPrefs* prefs,
190 const std::string& extension_id) {
192 std::string string_path;
193 if (prefs->ReadPrefAsString(extension_id,
194 kLastChooseEntryDirectory,
196 path = base::FilePath::FromUTF8Unsafe(string_path);
201 void SetLastChooseEntryDirectory(ExtensionPrefs* prefs,
202 const std::string& extension_id,
203 const base::FilePath& path) {
204 prefs->UpdateExtensionPref(extension_id,
205 kLastChooseEntryDirectory,
206 base::CreateFilePathValue(path));
209 std::vector<base::FilePath> GetGrayListedDirectories() {
210 std::vector<base::FilePath> graylisted_directories;
211 for (size_t i = 0; i < arraysize(kGraylistedPaths); ++i) {
212 base::FilePath graylisted_path;
213 if (PathService::Get(kGraylistedPaths[i], &graylisted_path))
214 graylisted_directories.push_back(graylisted_path);
216 return graylisted_directories;
219 } // namespace file_system_api
221 bool FileSystemGetDisplayPathFunction::RunSync() {
222 std::string filesystem_name;
223 std::string filesystem_path;
224 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
225 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
227 base::FilePath file_path;
228 if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
235 file_path = path_util::PrettifyPath(file_path);
236 SetResult(new base::StringValue(file_path.value()));
240 FileSystemEntryFunction::FileSystemEntryFunction()
242 is_directory_(false),
245 void FileSystemEntryFunction::PrepareFilesForWritableApp(
246 const std::vector<base::FilePath>& paths) {
247 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
248 app_file_handler_util::PrepareFilesForWritableApp(
252 base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
255 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
258 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
259 const std::vector<base::FilePath>& paths) {
260 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
261 if (!render_view_host_)
265 for (std::vector<base::FilePath>::const_iterator it = paths.begin();
266 it != paths.end(); ++it) {
267 AddEntryToResponse(*it, "");
272 void FileSystemEntryFunction::CreateResponse() {
274 response_ = new base::DictionaryValue();
275 base::ListValue* list = new base::ListValue();
276 response_->Set("entries", list);
277 response_->SetBoolean("multiple", multiple_);
278 SetResult(response_);
281 void FileSystemEntryFunction::AddEntryToResponse(
282 const base::FilePath& path,
283 const std::string& id_override) {
285 GrantedFileEntry file_entry = app_file_handler_util::CreateFileEntry(
288 render_view_host_->GetProcess()->GetID(),
291 base::ListValue* entries;
292 bool success = response_->GetList("entries", &entries);
295 base::DictionaryValue* entry = new base::DictionaryValue();
296 entry->SetString("fileSystemId", file_entry.filesystem_id);
297 entry->SetString("baseName", file_entry.registered_name);
298 if (id_override.empty())
299 entry->SetString("id", file_entry.id);
301 entry->SetString("id", id_override);
302 entry->SetBoolean("isDirectory", is_directory_);
303 entries->Append(entry);
306 void FileSystemEntryFunction::HandleWritableFileError(
307 const base::FilePath& error_path) {
308 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
309 error_ = base::StringPrintf(kWritableFileErrorFormat,
310 error_path.BaseName().AsUTF8Unsafe().c_str());
314 bool FileSystemGetWritableEntryFunction::RunAsync() {
315 std::string filesystem_name;
316 std::string filesystem_path;
317 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
318 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
320 if (!app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
321 error_ = kRequiresFileSystemWriteError;
325 if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
332 content::BrowserThread::PostTaskAndReply(
333 content::BrowserThread::FILE,
336 &FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread,
339 &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse,
344 void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
345 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
347 !extension_->permissions_data()->HasAPIPermission(
348 APIPermission::kFileSystemDirectory)) {
349 error_ = kRequiresFileSystemDirectoryError;
352 std::vector<base::FilePath> paths;
353 paths.push_back(path_);
354 PrepareFilesForWritableApp(paths);
357 void FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread() {
358 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
359 if (base::DirectoryExists(path_)) {
360 is_directory_ = true;
364 bool FileSystemIsWritableEntryFunction::RunSync() {
365 std::string filesystem_name;
366 std::string filesystem_path;
367 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
368 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
370 std::string filesystem_id;
371 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
372 error_ = app_file_handler_util::kInvalidParameters;
376 content::ChildProcessSecurityPolicy* policy =
377 content::ChildProcessSecurityPolicy::GetInstance();
378 int renderer_id = render_view_host_->GetProcess()->GetID();
379 bool is_writable = policy->CanReadWriteFileSystem(renderer_id,
382 SetResult(new base::FundamentalValue(is_writable));
386 // Handles showing a dialog to the user to ask for the filename for a file to
388 class FileSystemChooseEntryFunction::FilePicker
389 : public ui::SelectFileDialog::Listener {
391 FilePicker(FileSystemChooseEntryFunction* function,
392 content::WebContents* web_contents,
393 const base::FilePath& suggested_name,
394 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
395 ui::SelectFileDialog::Type picker_type)
396 : function_(function) {
397 select_file_dialog_ = ui::SelectFileDialog::Create(
398 this, new ChromeSelectFilePolicy(web_contents));
399 gfx::NativeWindow owning_window = web_contents ?
400 platform_util::GetTopLevel(web_contents->GetNativeView()) :
403 if (g_skip_picker_for_test) {
404 if (g_use_suggested_path_for_test) {
405 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
407 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
408 base::Unretained(this), suggested_name, 1,
409 static_cast<void*>(NULL)));
410 } else if (g_path_to_be_picked_for_test) {
411 content::BrowserThread::PostTask(
412 content::BrowserThread::UI, FROM_HERE,
414 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
415 base::Unretained(this), *g_path_to_be_picked_for_test, 1,
416 static_cast<void*>(NULL)));
417 } else if (g_paths_to_be_picked_for_test) {
418 content::BrowserThread::PostTask(
419 content::BrowserThread::UI,
422 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected,
423 base::Unretained(this),
424 *g_paths_to_be_picked_for_test,
425 static_cast<void*>(NULL)));
427 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
429 &FileSystemChooseEntryFunction::FilePicker::
430 FileSelectionCanceled,
431 base::Unretained(this), static_cast<void*>(NULL)));
436 select_file_dialog_->SelectFile(picker_type,
441 base::FilePath::StringType(),
446 ~FilePicker() override {}
449 // ui::SelectFileDialog::Listener implementation.
450 void FileSelected(const base::FilePath& path,
452 void* params) override {
453 std::vector<base::FilePath> paths;
454 paths.push_back(path);
455 MultiFilesSelected(paths, params);
458 void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
460 void* params) override {
461 // Normally, file.local_path is used because it is a native path to the
462 // local read-only cached file in the case of remote file system like
463 // Chrome OS's Google Drive integration. Here, however, |file.file_path| is
464 // necessary because we need to create a FileEntry denoting the remote file,
465 // not its cache. On other platforms than Chrome OS, they are the same.
467 // TODO(kinaba): remove this, once after the file picker implements proper
468 // switch of the path treatment depending on the |support_drive| flag.
469 FileSelected(file.file_path, index, params);
472 void MultiFilesSelected(const std::vector<base::FilePath>& files,
473 void* params) override {
474 function_->FilesSelected(files);
478 void MultiFilesSelectedWithExtraInfo(
479 const std::vector<ui::SelectedFileInfo>& files,
480 void* params) override {
481 std::vector<base::FilePath> paths;
482 for (std::vector<ui::SelectedFileInfo>::const_iterator it = files.begin();
483 it != files.end(); ++it) {
484 paths.push_back(it->file_path);
486 MultiFilesSelected(paths, params);
489 void FileSelectionCanceled(void* params) override {
490 function_->FileSelectionCanceled();
494 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
495 scoped_refptr<FileSystemChooseEntryFunction> function_;
497 DISALLOW_COPY_AND_ASSIGN(FilePicker);
500 void FileSystemChooseEntryFunction::ShowPicker(
501 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
502 ui::SelectFileDialog::Type picker_type) {
503 // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010
504 // we're adding the ability for a whitelisted extension to use this API since
505 // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd
506 // like a better solution and likely this code will go back to being
507 // platform-app only.
508 content::WebContents* web_contents = NULL;
509 if (extension_->is_platform_app()) {
510 AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
512 AppWindow* app_window =
513 registry->GetAppWindowForRenderViewHost(render_view_host());
515 error_ = kInvalidCallingPage;
519 web_contents = app_window->web_contents();
521 web_contents = GetAssociatedWebContents();
523 // The file picker will hold a reference to this function instance, preventing
524 // its destruction (and subsequent sending of the function response) until the
525 // user has selected a file or cancelled the picker. At that point, the picker
526 // will delete itself, which will also free the function instance.
528 this, web_contents, initial_path_, file_type_info, picker_type);
532 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
533 base::FilePath* path) {
534 g_skip_picker_for_test = true;
535 g_use_suggested_path_for_test = false;
536 g_path_to_be_picked_for_test = path;
537 g_paths_to_be_picked_for_test = NULL;
540 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
541 std::vector<base::FilePath>* paths) {
542 g_skip_picker_for_test = true;
543 g_use_suggested_path_for_test = false;
544 g_paths_to_be_picked_for_test = paths;
548 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() {
549 g_skip_picker_for_test = true;
550 g_use_suggested_path_for_test = true;
551 g_path_to_be_picked_for_test = NULL;
552 g_paths_to_be_picked_for_test = NULL;
556 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() {
557 g_skip_picker_for_test = true;
558 g_use_suggested_path_for_test = false;
559 g_path_to_be_picked_for_test = NULL;
560 g_paths_to_be_picked_for_test = NULL;
564 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
565 g_skip_picker_for_test = false;
569 void FileSystemChooseEntryFunction::SkipDirectoryConfirmationForTest() {
570 g_skip_directory_confirmation_for_test = true;
571 g_allow_directory_access_for_test = true;
575 void FileSystemChooseEntryFunction::AutoCancelDirectoryConfirmationForTest() {
576 g_skip_directory_confirmation_for_test = true;
577 g_allow_directory_access_for_test = false;
581 void FileSystemChooseEntryFunction::StopSkippingDirectoryConfirmationForTest() {
582 g_skip_directory_confirmation_for_test = false;
586 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
587 const std::string& name, const base::FilePath& path) {
588 // For testing on Chrome OS, where to deal with remote and local paths
589 // smoothly, all accessed paths need to be registered in the list of
590 // external mount points.
591 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
593 storage::kFileSystemTypeNativeLocal,
594 storage::FileSystemMountOption(),
598 void FileSystemChooseEntryFunction::SetInitialPathOnFileThread(
599 const base::FilePath& suggested_name,
600 const base::FilePath& previous_path) {
601 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
602 if (!previous_path.empty() && base::DirectoryExists(previous_path)) {
603 initial_path_ = previous_path.Append(suggested_name);
605 base::FilePath documents_dir;
606 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) {
607 initial_path_ = documents_dir.Append(suggested_name);
609 initial_path_ = suggested_name;
614 void FileSystemChooseEntryFunction::FilesSelected(
615 const std::vector<base::FilePath>& paths) {
616 DCHECK(!paths.empty());
617 base::FilePath last_choose_directory;
619 last_choose_directory = paths[0];
621 last_choose_directory = paths[0].DirName();
623 file_system_api::SetLastChooseEntryDirectory(
624 ExtensionPrefs::Get(GetProfile()),
626 last_choose_directory);
628 // Get the WebContents for the app window to be the parent window of the
629 // confirmation dialog if necessary.
630 AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
632 AppWindow* app_window =
633 registry->GetAppWindowForRenderViewHost(render_view_host());
635 error_ = kInvalidCallingPage;
639 content::WebContents* web_contents = app_window->web_contents();
641 DCHECK_EQ(paths.size(), 1u);
642 #if defined(OS_CHROMEOS)
643 base::FilePath check_path =
644 file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), paths[0])
646 : base::MakeAbsoluteFilePath(paths[0]);
648 base::FilePath check_path = base::MakeAbsoluteFilePath(paths[0]);
651 content::BrowserThread::PostTask(
652 content::BrowserThread::FILE,
655 &FileSystemChooseEntryFunction::ConfirmDirectoryAccessOnFileThread,
663 OnDirectoryAccessConfirmed(paths);
666 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
667 error_ = kUserCancelled;
671 void FileSystemChooseEntryFunction::ConfirmDirectoryAccessOnFileThread(
672 const base::FilePath& check_path,
673 const std::vector<base::FilePath>& paths,
674 content::WebContents* web_contents) {
675 if (check_path.empty()) {
676 content::BrowserThread::PostTask(
677 content::BrowserThread::UI,
679 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
684 for (size_t i = 0; i < arraysize(kGraylistedPaths); i++) {
685 base::FilePath graylisted_path;
686 if (PathService::Get(kGraylistedPaths[i], &graylisted_path) &&
687 (check_path == graylisted_path ||
688 check_path.IsParent(graylisted_path))) {
689 if (g_skip_directory_confirmation_for_test) {
690 if (g_allow_directory_access_for_test) {
693 content::BrowserThread::PostTask(
694 content::BrowserThread::UI,
696 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
702 content::BrowserThread::PostTask(
703 content::BrowserThread::UI,
706 CreateDirectoryAccessConfirmationDialog,
707 app_file_handler_util::HasFileSystemWritePermission(
709 base::UTF8ToUTF16(extension_->name()),
712 &FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
715 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
721 content::BrowserThread::PostTask(
722 content::BrowserThread::UI,
724 base::Bind(&FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
728 void FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed(
729 const std::vector<base::FilePath>& paths) {
730 if (app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
731 PrepareFilesForWritableApp(paths);
735 // Don't need to check the file, it's for reading.
736 RegisterFileSystemsAndSendResponse(paths);
739 void FileSystemChooseEntryFunction::BuildFileTypeInfo(
740 ui::SelectFileDialog::FileTypeInfo* file_type_info,
741 const base::FilePath::StringType& suggested_extension,
742 const AcceptOptions* accepts,
743 const bool* acceptsAllTypes) {
744 file_type_info->include_all_files = true;
746 file_type_info->include_all_files = *acceptsAllTypes;
748 bool need_suggestion = !file_type_info->include_all_files &&
749 !suggested_extension.empty();
752 typedef file_system::AcceptOption AcceptOption;
753 for (std::vector<linked_ptr<AcceptOption> >::const_iterator iter =
754 accepts->begin(); iter != accepts->end(); ++iter) {
755 base::string16 description;
756 std::vector<base::FilePath::StringType> extensions;
758 if (!GetFileTypesFromAcceptOption(**iter, &extensions, &description))
759 continue; // No extensions were found.
761 file_type_info->extensions.push_back(extensions);
762 file_type_info->extension_description_overrides.push_back(description);
764 // If we still need to find suggested_extension, hunt for it inside the
765 // extensions returned from GetFileTypesFromAcceptOption.
766 if (need_suggestion && std::find(extensions.begin(),
767 extensions.end(), suggested_extension) != extensions.end()) {
768 need_suggestion = false;
773 // If there's nothing in our accepted extension list or we couldn't find the
774 // suggested extension required, then default to accepting all types.
775 if (file_type_info->extensions.empty() || need_suggestion)
776 file_type_info->include_all_files = true;
779 void FileSystemChooseEntryFunction::BuildSuggestion(
780 const std::string *opt_name,
781 base::FilePath* suggested_name,
782 base::FilePath::StringType* suggested_extension) {
784 *suggested_name = base::FilePath::FromUTF8Unsafe(*opt_name);
786 // Don't allow any path components; shorten to the base name. This should
787 // result in a relative path, but in some cases may not. Clear the
788 // suggestion for safety if this is the case.
789 *suggested_name = suggested_name->BaseName();
790 if (suggested_name->IsAbsolute())
791 *suggested_name = base::FilePath();
793 *suggested_extension = suggested_name->Extension();
794 if (!suggested_extension->empty())
795 suggested_extension->erase(suggested_extension->begin()); // drop the .
799 bool FileSystemChooseEntryFunction::RunAsync() {
800 scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_));
801 EXTENSION_FUNCTION_VALIDATE(params.get());
803 base::FilePath suggested_name;
804 ui::SelectFileDialog::FileTypeInfo file_type_info;
805 ui::SelectFileDialog::Type picker_type =
806 ui::SelectFileDialog::SELECT_OPEN_FILE;
808 file_system::ChooseEntryOptions* options = params->options.get();
810 multiple_ = options->accepts_multiple;
812 picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
814 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE &&
815 !app_file_handler_util::HasFileSystemWritePermission(
817 error_ = kRequiresFileSystemWriteError;
819 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
820 if (!app_file_handler_util::HasFileSystemWritePermission(
822 error_ = kRequiresFileSystemWriteError;
826 error_ = kMultipleUnsupportedError;
829 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
830 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY) {
831 is_directory_ = true;
832 if (!extension_->permissions_data()->HasAPIPermission(
833 APIPermission::kFileSystemDirectory)) {
834 error_ = kRequiresFileSystemDirectoryError;
838 error_ = kMultipleUnsupportedError;
841 picker_type = ui::SelectFileDialog::SELECT_FOLDER;
844 base::FilePath::StringType suggested_extension;
845 BuildSuggestion(options->suggested_name.get(), &suggested_name,
846 &suggested_extension);
848 BuildFileTypeInfo(&file_type_info, suggested_extension,
849 options->accepts.get(), options->accepts_all_types.get());
852 file_type_info.support_drive = true;
854 base::FilePath previous_path = file_system_api::GetLastChooseEntryDirectory(
855 ExtensionPrefs::Get(GetProfile()), extension()->id());
857 content::BrowserThread::PostTaskAndReply(
858 content::BrowserThread::FILE,
860 base::Bind(&FileSystemChooseEntryFunction::SetInitialPathOnFileThread,
864 base::Bind(&FileSystemChooseEntryFunction::ShowPicker,
871 bool FileSystemRetainEntryFunction::RunAsync() {
872 std::string entry_id;
873 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
874 SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile());
875 // Add the file to the retain list if it is not already on there.
876 if (!saved_files_service->IsRegistered(extension_->id(), entry_id)) {
877 std::string filesystem_name;
878 std::string filesystem_path;
880 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
881 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
882 if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
890 std::string filesystem_id;
891 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id))
895 extensions::util::GetSiteForExtensionId(extension_id(), GetProfile());
896 storage::FileSystemContext* const context =
897 content::BrowserContext::GetStoragePartitionForSite(GetProfile(), site)
898 ->GetFileSystemContext();
899 const storage::FileSystemURL url = context->CreateCrackedFileSystemURL(
901 storage::kFileSystemTypeIsolated,
902 IsolatedContext::GetInstance()
903 ->CreateVirtualRootPath(filesystem_id)
904 .Append(base::FilePath::FromUTF8Unsafe(filesystem_path)));
906 content::BrowserThread::PostTask(
907 content::BrowserThread::IO,
911 &storage::FileSystemOperationRunner::GetMetadata),
912 context->operation_runner()->AsWeakPtr(),
915 &PassFileInfoToUIThread,
916 base::Bind(&FileSystemRetainEntryFunction::RetainFileEntry,
923 saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
928 void FileSystemRetainEntryFunction::RetainFileEntry(
929 const std::string& entry_id,
930 const base::FilePath& path,
931 scoped_ptr<base::File::Info> file_info) {
937 SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile());
938 saved_files_service->RegisterFileEntry(
939 extension_->id(), entry_id, path, file_info->is_directory);
940 saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
944 bool FileSystemIsRestorableFunction::RunSync() {
945 std::string entry_id;
946 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
947 SetResult(new base::FundamentalValue(SavedFilesService::Get(
948 GetProfile())->IsRegistered(extension_->id(), entry_id)));
952 bool FileSystemRestoreEntryFunction::RunAsync() {
953 std::string entry_id;
954 bool needs_new_entry;
955 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
956 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &needs_new_entry));
957 const SavedFileEntry* file_entry = SavedFilesService::Get(
958 GetProfile())->GetFileEntry(extension_->id(), entry_id);
960 error_ = kUnknownIdError;
964 SavedFilesService::Get(GetProfile())
965 ->EnqueueFileEntry(extension_->id(), entry_id);
967 // Only create a new file entry if the renderer requests one.
968 // |needs_new_entry| will be false if the renderer already has an Entry for
970 if (needs_new_entry) {
971 is_directory_ = file_entry->is_directory;
973 AddEntryToResponse(file_entry->path, file_entry->id);
979 bool FileSystemObserveDirectoryFunction::RunSync() {
981 error_ = kUnknownIdError;
985 bool FileSystemUnobserveEntryFunction::RunSync() {
987 error_ = kUnknownIdError;
991 bool FileSystemGetObservedEntriesFunction::RunSync() {
993 error_ = kUnknownIdError;
997 } // namespace extensions