Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / file_system / file_system_api.cc
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.
4
5 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
6
7 #include <set>
8
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"
54
55 #if defined(OS_MACOSX)
56 #include <CoreFoundation/CoreFoundation.h>
57 #include "base/mac/foundation_util.h"
58 #endif
59
60 #if defined(OS_CHROMEOS)
61 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
62 #endif
63
64 using apps::SavedFileEntry;
65 using apps::SavedFilesService;
66 using storage::IsolatedContext;
67
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";
79
80 namespace file_system = extensions::api::file_system;
81 namespace ChooseEntry = file_system::ChooseEntry;
82
83 namespace {
84
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;
91
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
94 // were found.
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;
101
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);
111       if (inner.empty())
112         continue;
113
114       if (valid_type)
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;
123
124       extension_set.insert(inner.begin(), inner.end());
125       valid_type = true;
126     }
127   }
128
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);
135 #if defined(OS_WIN)
136       extension_set.insert(base::UTF8ToWide(*iter));
137 #else
138       extension_set.insert(*iter);
139 #endif
140     }
141   }
142
143   extensions->assign(extension_set.begin(), extension_set.end());
144   if (extensions->empty())
145     return false;
146
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);
151
152   return true;
153 }
154
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";
158
159 const int kGraylistedPaths[] = {
160   base::DIR_HOME,
161 #if defined(OS_WIN)
162   base::DIR_PROGRAM_FILES,
163   base::DIR_PROGRAM_FILESX86,
164   base::DIR_WINDOWS,
165 #endif
166 };
167
168 typedef base::Callback<void(scoped_ptr<base::File::Info>)> FileInfoOptCallback;
169
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,
179       FROM_HERE,
180       base::Bind(callback, base::Passed(&file_info)));
181 }
182
183 }  // namespace
184
185 namespace extensions {
186
187 namespace file_system_api {
188
189 base::FilePath GetLastChooseEntryDirectory(const ExtensionPrefs* prefs,
190                                            const std::string& extension_id) {
191   base::FilePath path;
192   std::string string_path;
193   if (prefs->ReadPrefAsString(extension_id,
194                               kLastChooseEntryDirectory,
195                               &string_path)) {
196     path = base::FilePath::FromUTF8Unsafe(string_path);
197   }
198   return path;
199 }
200
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));
207 }
208
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);
215   }
216   return graylisted_directories;
217 }
218
219 }  // namespace file_system_api
220
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));
226
227   base::FilePath file_path;
228   if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
229                                                           filesystem_path,
230                                                           render_view_host_,
231                                                           &file_path,
232                                                           &error_))
233     return false;
234
235   file_path = path_util::PrettifyPath(file_path);
236   SetResult(new base::StringValue(file_path.value()));
237   return true;
238 }
239
240 FileSystemEntryFunction::FileSystemEntryFunction()
241     : multiple_(false),
242       is_directory_(false),
243       response_(NULL) {}
244
245 void FileSystemEntryFunction::PrepareFilesForWritableApp(
246     const std::vector<base::FilePath>& paths) {
247   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
248   app_file_handler_util::PrepareFilesForWritableApp(
249       paths,
250       GetProfile(),
251       is_directory_,
252       base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
253                  this,
254                  paths),
255       base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
256 }
257
258 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
259     const std::vector<base::FilePath>& paths) {
260   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
261   if (!render_view_host_)
262     return;
263
264   CreateResponse();
265   for (std::vector<base::FilePath>::const_iterator it = paths.begin();
266        it != paths.end(); ++it) {
267     AddEntryToResponse(*it, "");
268   }
269   SendResponse(true);
270 }
271
272 void FileSystemEntryFunction::CreateResponse() {
273   DCHECK(!response_);
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_);
279 }
280
281 void FileSystemEntryFunction::AddEntryToResponse(
282     const base::FilePath& path,
283     const std::string& id_override) {
284   DCHECK(response_);
285   GrantedFileEntry file_entry = app_file_handler_util::CreateFileEntry(
286       GetProfile(),
287       extension(),
288       render_view_host_->GetProcess()->GetID(),
289       path,
290       is_directory_);
291   base::ListValue* entries;
292   bool success = response_->GetList("entries", &entries);
293   DCHECK(success);
294
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);
300   else
301     entry->SetString("id", id_override);
302   entry->SetBoolean("isDirectory", is_directory_);
303   entries->Append(entry);
304 }
305
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());
311   SendResponse(false);
312 }
313
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));
319
320   if (!app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
321     error_ = kRequiresFileSystemWriteError;
322     return false;
323   }
324
325   if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
326                                                           filesystem_path,
327                                                           render_view_host_,
328                                                           &path_,
329                                                           &error_))
330     return false;
331
332   content::BrowserThread::PostTaskAndReply(
333       content::BrowserThread::FILE,
334       FROM_HERE,
335       base::Bind(
336           &FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread,
337           this),
338       base::Bind(
339           &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse,
340           this));
341   return true;
342 }
343
344 void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
345   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
346   if (is_directory_ &&
347       !extension_->permissions_data()->HasAPIPermission(
348           APIPermission::kFileSystemDirectory)) {
349     error_ = kRequiresFileSystemDirectoryError;
350     SendResponse(false);
351   }
352   std::vector<base::FilePath> paths;
353   paths.push_back(path_);
354   PrepareFilesForWritableApp(paths);
355 }
356
357 void FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread() {
358   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
359   if (base::DirectoryExists(path_)) {
360     is_directory_ = true;
361   }
362 }
363
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));
369
370   std::string filesystem_id;
371   if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
372     error_ = app_file_handler_util::kInvalidParameters;
373     return false;
374   }
375
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,
380                                                     filesystem_id);
381
382   SetResult(new base::FundamentalValue(is_writable));
383   return true;
384 }
385
386 // Handles showing a dialog to the user to ask for the filename for a file to
387 // save or open.
388 class FileSystemChooseEntryFunction::FilePicker
389     : public ui::SelectFileDialog::Listener {
390  public:
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()) :
401         NULL;
402
403     if (g_skip_picker_for_test) {
404       if (g_use_suggested_path_for_test) {
405         content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
406             base::Bind(
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,
413             base::Bind(
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,
420             FROM_HERE,
421             base::Bind(
422                 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected,
423                 base::Unretained(this),
424                 *g_paths_to_be_picked_for_test,
425                 static_cast<void*>(NULL)));
426       } else {
427         content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
428             base::Bind(
429                 &FileSystemChooseEntryFunction::FilePicker::
430                     FileSelectionCanceled,
431                 base::Unretained(this), static_cast<void*>(NULL)));
432       }
433       return;
434     }
435
436     select_file_dialog_->SelectFile(picker_type,
437                                     base::string16(),
438                                     suggested_name,
439                                     &file_type_info,
440                                     0,
441                                     base::FilePath::StringType(),
442                                     owning_window,
443                                     NULL);
444   }
445
446   ~FilePicker() override {}
447
448  private:
449   // ui::SelectFileDialog::Listener implementation.
450   void FileSelected(const base::FilePath& path,
451                     int index,
452                     void* params) override {
453     std::vector<base::FilePath> paths;
454     paths.push_back(path);
455     MultiFilesSelected(paths, params);
456   }
457
458   void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
459                                  int index,
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.
466     //
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);
470   }
471
472   void MultiFilesSelected(const std::vector<base::FilePath>& files,
473                           void* params) override {
474     function_->FilesSelected(files);
475     delete this;
476   }
477
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);
485     }
486     MultiFilesSelected(paths, params);
487   }
488
489   void FileSelectionCanceled(void* params) override {
490     function_->FileSelectionCanceled();
491     delete this;
492   }
493
494   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
495   scoped_refptr<FileSystemChooseEntryFunction> function_;
496
497   DISALLOW_COPY_AND_ASSIGN(FilePicker);
498 };
499
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());
511     DCHECK(registry);
512     AppWindow* app_window =
513         registry->GetAppWindowForRenderViewHost(render_view_host());
514     if (!app_window) {
515       error_ = kInvalidCallingPage;
516       SendResponse(false);
517       return;
518     }
519     web_contents = app_window->web_contents();
520   } else {
521     web_contents = GetAssociatedWebContents();
522   }
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.
527   new FilePicker(
528       this, web_contents, initial_path_, file_type_info, picker_type);
529 }
530
531 // static
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;
538 }
539
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;
545 }
546
547 // static
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;
553 }
554
555 // static
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;
561 }
562
563 // static
564 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
565   g_skip_picker_for_test = false;
566 }
567
568 // static
569 void FileSystemChooseEntryFunction::SkipDirectoryConfirmationForTest() {
570   g_skip_directory_confirmation_for_test = true;
571   g_allow_directory_access_for_test = true;
572 }
573
574 // static
575 void FileSystemChooseEntryFunction::AutoCancelDirectoryConfirmationForTest() {
576   g_skip_directory_confirmation_for_test = true;
577   g_allow_directory_access_for_test = false;
578 }
579
580 // static
581 void FileSystemChooseEntryFunction::StopSkippingDirectoryConfirmationForTest() {
582   g_skip_directory_confirmation_for_test = false;
583 }
584
585 // static
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(
592       name,
593       storage::kFileSystemTypeNativeLocal,
594       storage::FileSystemMountOption(),
595       path);
596 }
597
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);
604   } else {
605     base::FilePath documents_dir;
606     if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) {
607       initial_path_ = documents_dir.Append(suggested_name);
608     } else {
609       initial_path_ = suggested_name;
610     }
611   }
612 }
613
614 void FileSystemChooseEntryFunction::FilesSelected(
615     const std::vector<base::FilePath>& paths) {
616   DCHECK(!paths.empty());
617   base::FilePath last_choose_directory;
618   if (is_directory_) {
619     last_choose_directory = paths[0];
620   } else {
621     last_choose_directory = paths[0].DirName();
622   }
623   file_system_api::SetLastChooseEntryDirectory(
624       ExtensionPrefs::Get(GetProfile()),
625       extension()->id(),
626       last_choose_directory);
627   if (is_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());
631     DCHECK(registry);
632     AppWindow* app_window =
633         registry->GetAppWindowForRenderViewHost(render_view_host());
634     if (!app_window) {
635       error_ = kInvalidCallingPage;
636       SendResponse(false);
637       return;
638     }
639     content::WebContents* web_contents = app_window->web_contents();
640
641     DCHECK_EQ(paths.size(), 1u);
642 #if defined(OS_CHROMEOS)
643     base::FilePath check_path =
644         file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), paths[0])
645             ? paths[0]
646             : base::MakeAbsoluteFilePath(paths[0]);
647 #else
648     base::FilePath check_path = base::MakeAbsoluteFilePath(paths[0]);
649 #endif
650
651     content::BrowserThread::PostTask(
652         content::BrowserThread::FILE,
653         FROM_HERE,
654         base::Bind(
655             &FileSystemChooseEntryFunction::ConfirmDirectoryAccessOnFileThread,
656             this,
657             check_path,
658             paths,
659             web_contents));
660     return;
661   }
662
663   OnDirectoryAccessConfirmed(paths);
664 }
665
666 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
667   error_ = kUserCancelled;
668   SendResponse(false);
669 }
670
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,
678         FROM_HERE,
679         base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
680                    this));
681     return;
682   }
683
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) {
691           break;
692         } else {
693           content::BrowserThread::PostTask(
694               content::BrowserThread::UI,
695               FROM_HERE,
696               base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
697                          this));
698         }
699         return;
700       }
701
702       content::BrowserThread::PostTask(
703           content::BrowserThread::UI,
704           FROM_HERE,
705           base::Bind(
706               CreateDirectoryAccessConfirmationDialog,
707               app_file_handler_util::HasFileSystemWritePermission(
708                   extension_.get()),
709               base::UTF8ToUTF16(extension_->name()),
710               web_contents,
711               base::Bind(
712                   &FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
713                   this,
714                   paths),
715               base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
716                          this)));
717       return;
718     }
719   }
720
721   content::BrowserThread::PostTask(
722       content::BrowserThread::UI,
723       FROM_HERE,
724       base::Bind(&FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
725                  this, paths));
726 }
727
728 void FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed(
729     const std::vector<base::FilePath>& paths) {
730   if (app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
731     PrepareFilesForWritableApp(paths);
732     return;
733   }
734
735   // Don't need to check the file, it's for reading.
736   RegisterFileSystemsAndSendResponse(paths);
737 }
738
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;
745   if (acceptsAllTypes)
746     file_type_info->include_all_files = *acceptsAllTypes;
747
748   bool need_suggestion = !file_type_info->include_all_files &&
749                          !suggested_extension.empty();
750
751   if (accepts) {
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;
757
758       if (!GetFileTypesFromAcceptOption(**iter, &extensions, &description))
759         continue;  // No extensions were found.
760
761       file_type_info->extensions.push_back(extensions);
762       file_type_info->extension_description_overrides.push_back(description);
763
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;
769       }
770     }
771   }
772
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;
777 }
778
779 void FileSystemChooseEntryFunction::BuildSuggestion(
780     const std::string *opt_name,
781     base::FilePath* suggested_name,
782     base::FilePath::StringType* suggested_extension) {
783   if (opt_name) {
784     *suggested_name = base::FilePath::FromUTF8Unsafe(*opt_name);
785
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();
792
793     *suggested_extension = suggested_name->Extension();
794     if (!suggested_extension->empty())
795       suggested_extension->erase(suggested_extension->begin());  // drop the .
796   }
797 }
798
799 bool FileSystemChooseEntryFunction::RunAsync() {
800   scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_));
801   EXTENSION_FUNCTION_VALIDATE(params.get());
802
803   base::FilePath suggested_name;
804   ui::SelectFileDialog::FileTypeInfo file_type_info;
805   ui::SelectFileDialog::Type picker_type =
806       ui::SelectFileDialog::SELECT_OPEN_FILE;
807
808   file_system::ChooseEntryOptions* options = params->options.get();
809   if (options) {
810     multiple_ = options->accepts_multiple;
811     if (multiple_)
812       picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
813
814     if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE &&
815         !app_file_handler_util::HasFileSystemWritePermission(
816             extension_.get())) {
817       error_ = kRequiresFileSystemWriteError;
818       return false;
819     } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
820       if (!app_file_handler_util::HasFileSystemWritePermission(
821               extension_.get())) {
822         error_ = kRequiresFileSystemWriteError;
823         return false;
824       }
825       if (multiple_) {
826         error_ = kMultipleUnsupportedError;
827         return false;
828       }
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;
835         return false;
836       }
837       if (multiple_) {
838         error_ = kMultipleUnsupportedError;
839         return false;
840       }
841       picker_type = ui::SelectFileDialog::SELECT_FOLDER;
842     }
843
844     base::FilePath::StringType suggested_extension;
845     BuildSuggestion(options->suggested_name.get(), &suggested_name,
846         &suggested_extension);
847
848     BuildFileTypeInfo(&file_type_info, suggested_extension,
849         options->accepts.get(), options->accepts_all_types.get());
850   }
851
852   file_type_info.support_drive = true;
853
854   base::FilePath previous_path = file_system_api::GetLastChooseEntryDirectory(
855       ExtensionPrefs::Get(GetProfile()), extension()->id());
856
857   content::BrowserThread::PostTaskAndReply(
858       content::BrowserThread::FILE,
859       FROM_HERE,
860       base::Bind(&FileSystemChooseEntryFunction::SetInitialPathOnFileThread,
861                  this,
862                  suggested_name,
863                  previous_path),
864       base::Bind(&FileSystemChooseEntryFunction::ShowPicker,
865                  this,
866                  file_type_info,
867                  picker_type));
868   return true;
869 }
870
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;
879     base::FilePath 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,
883                                                             filesystem_path,
884                                                             render_view_host_,
885                                                             &path,
886                                                             &error_)) {
887       return false;
888     }
889
890     std::string filesystem_id;
891     if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id))
892       return false;
893
894     const GURL site =
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(
900         site,
901         storage::kFileSystemTypeIsolated,
902         IsolatedContext::GetInstance()
903             ->CreateVirtualRootPath(filesystem_id)
904             .Append(base::FilePath::FromUTF8Unsafe(filesystem_path)));
905
906     content::BrowserThread::PostTask(
907         content::BrowserThread::IO,
908         FROM_HERE,
909         base::Bind(
910             base::IgnoreResult(
911                 &storage::FileSystemOperationRunner::GetMetadata),
912             context->operation_runner()->AsWeakPtr(),
913             url,
914             base::Bind(
915                 &PassFileInfoToUIThread,
916                 base::Bind(&FileSystemRetainEntryFunction::RetainFileEntry,
917                            this,
918                            entry_id,
919                            path))));
920     return true;
921   }
922
923   saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
924   SendResponse(true);
925   return true;
926 }
927
928 void FileSystemRetainEntryFunction::RetainFileEntry(
929     const std::string& entry_id,
930     const base::FilePath& path,
931     scoped_ptr<base::File::Info> file_info) {
932   if (!file_info) {
933     SendResponse(false);
934     return;
935   }
936
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);
941   SendResponse(true);
942 }
943
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)));
949   return true;
950 }
951
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);
959   if (!file_entry) {
960     error_ = kUnknownIdError;
961     return false;
962   }
963
964   SavedFilesService::Get(GetProfile())
965       ->EnqueueFileEntry(extension_->id(), entry_id);
966
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
969   // |entry_id|.
970   if (needs_new_entry) {
971     is_directory_ = file_entry->is_directory;
972     CreateResponse();
973     AddEntryToResponse(file_entry->path, file_entry->id);
974   }
975   SendResponse(true);
976   return true;
977 }
978
979 bool FileSystemObserveDirectoryFunction::RunSync() {
980   NOTIMPLEMENTED();
981   error_ = kUnknownIdError;
982   return false;
983 }
984
985 bool FileSystemUnobserveEntryFunction::RunSync() {
986   NOTIMPLEMENTED();
987   error_ = kUnknownIdError;
988   return false;
989 }
990
991 bool FileSystemGetObservedEntriesFunction::RunSync() {
992   NOTIMPLEMENTED();
993   error_ = kUnknownIdError;
994   return false;
995 }
996
997 }  // namespace extensions