Home | History | Annotate | Download | only in file_system
      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 "apps/saved_files_service.h"
      8 #include "apps/shell_window.h"
      9 #include "apps/shell_window_registry.h"
     10 #include "base/bind.h"
     11 #include "base/file_util.h"
     12 #include "base/files/file_path.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_system.h"
     24 #include "chrome/browser/platform_util.h"
     25 #include "chrome/browser/ui/chrome_select_file_policy.h"
     26 #include "chrome/common/chrome_paths.h"
     27 #include "chrome/common/extensions/api/file_system.h"
     28 #include "chrome/common/extensions/permissions/api_permission.h"
     29 #include "content/public/browser/browser_thread.h"
     30 #include "content/public/browser/child_process_security_policy.h"
     31 #include "content/public/browser/render_process_host.h"
     32 #include "content/public/browser/render_view_host.h"
     33 #include "content/public/browser/web_contents.h"
     34 #include "content/public/browser/web_contents_view.h"
     35 #include "grit/generated_resources.h"
     36 #include "net/base/mime_util.h"
     37 #include "ui/base/l10n/l10n_util.h"
     38 #include "ui/shell_dialogs/select_file_dialog.h"
     39 #include "ui/shell_dialogs/selected_file_info.h"
     40 #include "webkit/browser/fileapi/external_mount_points.h"
     41 #include "webkit/browser/fileapi/isolated_context.h"
     42 #include "webkit/common/fileapi/file_system_types.h"
     43 #include "webkit/common/fileapi/file_system_util.h"
     44 
     45 #if defined(OS_MACOSX)
     46 #include <CoreFoundation/CoreFoundation.h>
     47 #include "base/mac/foundation_util.h"
     48 #endif
     49 
     50 #if defined(OS_CHROMEOS)
     51 #include "chrome/browser/chromeos/drive/file_system_util.h"
     52 #endif
     53 
     54 using apps::SavedFileEntry;
     55 using apps::SavedFilesService;
     56 using apps::ShellWindow;
     57 using fileapi::IsolatedContext;
     58 
     59 const char kInvalidParameters[] = "Invalid parameters";
     60 const char kSecurityError[] = "Security error";
     61 const char kInvalidCallingPage[] = "Invalid calling page. This function can't "
     62     "be called from a background page.";
     63 const char kUserCancelled[] = "User cancelled";
     64 const char kWritableFileRestrictedLocationError[] =
     65     "Cannot write to file in a restricted location";
     66 const char kWritableFileErrorFormat[] = "Error opening %s";
     67 const char kRequiresFileSystemWriteError[] =
     68     "Operation requires fileSystem.write permission";
     69 const char kMultipleUnsupportedError[] =
     70     "acceptsMultiple: true is not supported for 'saveFile'";
     71 const char kUnknownIdError[] = "Unknown id";
     72 
     73 namespace file_system = extensions::api::file_system;
     74 namespace ChooseEntry = file_system::ChooseEntry;
     75 
     76 namespace {
     77 
     78 const int kBlacklistedPaths[] = {
     79   chrome::DIR_APP,
     80   chrome::DIR_USER_DATA,
     81 };
     82 
     83 #if defined(OS_CHROMEOS)
     84 // On Chrome OS, the default downloads directory is a subdirectory of user data
     85 // directory, and should be whitelisted.
     86 const int kWhitelistedPaths[] = {
     87   chrome::DIR_DEFAULT_DOWNLOADS_SAFE,
     88 };
     89 #endif
     90 
     91 #if defined(OS_MACOSX)
     92 // Retrieves the localized display name for the base name of the given path.
     93 // If the path is not localized, this will just return the base name.
     94 std::string GetDisplayBaseName(const base::FilePath& path) {
     95   base::ScopedCFTypeRef<CFURLRef> url(CFURLCreateFromFileSystemRepresentation(
     96       NULL, (const UInt8*)path.value().c_str(), path.value().length(), true));
     97   if (!url)
     98     return path.BaseName().value();
     99 
    100   CFStringRef str;
    101   if (LSCopyDisplayNameForURL(url, &str) != noErr)
    102     return path.BaseName().value();
    103 
    104   std::string result(base::SysCFStringRefToUTF8(str));
    105   CFRelease(str);
    106   return result;
    107 }
    108 
    109 // Prettifies |source_path| for OS X, by localizing every component of the
    110 // path. Additionally, if the path is inside the user's home directory, then
    111 // replace the home directory component with "~".
    112 base::FilePath PrettifyPath(const base::FilePath& source_path) {
    113   base::FilePath home_path;
    114   PathService::Get(base::DIR_HOME, &home_path);
    115   DCHECK(source_path.IsAbsolute());
    116 
    117   // Break down the incoming path into components, and grab the display name
    118   // for every component. This will match app bundles, ".localized" folders,
    119   // and localized subfolders of the user's home directory.
    120   // Don't grab the display name of the first component, i.e., "/", as it'll
    121   // show up as the HDD name.
    122   std::vector<base::FilePath::StringType> components;
    123   source_path.GetComponents(&components);
    124   base::FilePath display_path = base::FilePath(components[0]);
    125   base::FilePath actual_path = display_path;
    126   for (std::vector<base::FilePath::StringType>::iterator i =
    127            components.begin() + 1; i != components.end(); ++i) {
    128     actual_path = actual_path.Append(*i);
    129     if (actual_path == home_path) {
    130       display_path = base::FilePath("~");
    131       home_path = base::FilePath();
    132       continue;
    133     }
    134     std::string display = GetDisplayBaseName(actual_path);
    135     display_path = display_path.Append(display);
    136   }
    137   DCHECK_EQ(actual_path.value(), source_path.value());
    138   return display_path;
    139 }
    140 #else  // defined(OS_MACOSX)
    141 // Prettifies |source_path|, by replacing the user's home directory with "~"
    142 // (if applicable).
    143 base::FilePath PrettifyPath(const base::FilePath& source_path) {
    144 #if defined(OS_WIN) || defined(OS_POSIX)
    145 #if defined(OS_WIN)
    146   int home_key = base::DIR_PROFILE;
    147 #elif defined(OS_POSIX)
    148   int home_key = base::DIR_HOME;
    149 #endif
    150   base::FilePath home_path;
    151   base::FilePath display_path = base::FilePath::FromUTF8Unsafe("~");
    152   if (PathService::Get(home_key, &home_path)
    153       && home_path.AppendRelativePath(source_path, &display_path))
    154     return display_path;
    155 #endif
    156   return source_path;
    157 }
    158 #endif  // defined(OS_MACOSX)
    159 
    160 bool g_skip_picker_for_test = false;
    161 bool g_use_suggested_path_for_test = false;
    162 base::FilePath* g_path_to_be_picked_for_test;
    163 std::vector<base::FilePath>* g_paths_to_be_picked_for_test;
    164 
    165 bool GetFileSystemAndPathOfFileEntry(
    166     const std::string& filesystem_name,
    167     const std::string& filesystem_path,
    168     const content::RenderViewHost* render_view_host,
    169     std::string* filesystem_id,
    170     base::FilePath* file_path,
    171     std::string* error) {
    172   if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, filesystem_id)) {
    173     *error = kInvalidParameters;
    174     return false;
    175   }
    176 
    177   // Only return the display path if the process has read access to the
    178   // filesystem.
    179   content::ChildProcessSecurityPolicy* policy =
    180       content::ChildProcessSecurityPolicy::GetInstance();
    181   if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
    182                                  *filesystem_id)) {
    183     *error = kSecurityError;
    184     return false;
    185   }
    186 
    187   IsolatedContext* context = IsolatedContext::GetInstance();
    188   base::FilePath relative_path =
    189       base::FilePath::FromUTF8Unsafe(filesystem_path);
    190   base::FilePath virtual_path = context->CreateVirtualRootPath(*filesystem_id)
    191       .Append(relative_path);
    192   if (!context->CrackVirtualPath(virtual_path,
    193                                  filesystem_id,
    194                                  NULL,
    195                                  file_path)) {
    196     *error = kInvalidParameters;
    197     return false;
    198   }
    199 
    200   return true;
    201 }
    202 
    203 bool GetFilePathOfFileEntry(const std::string& filesystem_name,
    204                             const std::string& filesystem_path,
    205                             const content::RenderViewHost* render_view_host,
    206                             base::FilePath* file_path,
    207                             std::string* error) {
    208   std::string filesystem_id;
    209   return GetFileSystemAndPathOfFileEntry(filesystem_name,
    210                                          filesystem_path,
    211                                          render_view_host,
    212                                          &filesystem_id,
    213                                          file_path,
    214                                          error);
    215 }
    216 
    217 bool DoCheckWritableFile(const base::FilePath& path,
    218                          const base::FilePath& extension_directory,
    219                          std::string* error_message) {
    220   // Don't allow links.
    221   if (base::PathExists(path) && file_util::IsLink(path)) {
    222     *error_message = base::StringPrintf(kWritableFileErrorFormat,
    223                                         path.BaseName().AsUTF8Unsafe().c_str());
    224     return false;
    225   }
    226 
    227   if (extension_directory == path || extension_directory.IsParent(path)) {
    228     *error_message = kWritableFileRestrictedLocationError;
    229     return false;
    230   }
    231 
    232   bool is_whitelisted_path = false;
    233 
    234 #if defined(OS_CHROMEOS)
    235   for (size_t i = 0; i < arraysize(kWhitelistedPaths); i++) {
    236     base::FilePath whitelisted_path;
    237     if (PathService::Get(kWhitelistedPaths[i], &whitelisted_path) &&
    238         (whitelisted_path == path || whitelisted_path.IsParent(path))) {
    239       is_whitelisted_path = true;
    240       break;
    241     }
    242   }
    243 #endif
    244 
    245   if (!is_whitelisted_path) {
    246     for (size_t i = 0; i < arraysize(kBlacklistedPaths); i++) {
    247       base::FilePath blacklisted_path;
    248       if (PathService::Get(kBlacklistedPaths[i], &blacklisted_path) &&
    249           (blacklisted_path == path || blacklisted_path.IsParent(path))) {
    250         *error_message = kWritableFileRestrictedLocationError;
    251         return false;
    252       }
    253     }
    254   }
    255 
    256   // Create the file if it doesn't already exist.
    257   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    258   int creation_flags = base::PLATFORM_FILE_CREATE |
    259                        base::PLATFORM_FILE_READ |
    260                        base::PLATFORM_FILE_WRITE;
    261   base::PlatformFile file = base::CreatePlatformFile(path, creation_flags,
    262                                                      NULL, &error);
    263   // Close the file so we don't keep a lock open.
    264   if (file != base::kInvalidPlatformFileValue)
    265     base::ClosePlatformFile(file);
    266   if (error != base::PLATFORM_FILE_OK &&
    267       error != base::PLATFORM_FILE_ERROR_EXISTS) {
    268     *error_message = base::StringPrintf(kWritableFileErrorFormat,
    269                                         path.BaseName().AsUTF8Unsafe().c_str());
    270     return false;
    271   }
    272 
    273   return true;
    274 }
    275 
    276 // Checks whether a list of paths are all OK for writing and calls a provided
    277 // on_success or on_failure callback when done. A file is OK for writing if it
    278 // is not a symlink, is not in a blacklisted path and can be opened for writing;
    279 // files are created if they do not exist.
    280 class WritableFileChecker
    281     : public base::RefCountedThreadSafe<WritableFileChecker> {
    282  public:
    283   WritableFileChecker(
    284       const std::vector<base::FilePath>& paths,
    285       Profile* profile,
    286       const base::FilePath& extension_path,
    287       const base::Closure& on_success,
    288       const base::Callback<void(const std::string&)>& on_failure)
    289       : outstanding_tasks_(1),
    290         extension_path_(extension_path),
    291         on_success_(on_success),
    292         on_failure_(on_failure) {
    293 #if defined(OS_CHROMEOS)
    294     if (drive::util::IsUnderDriveMountPoint(paths[0])) {
    295       outstanding_tasks_ = paths.size();
    296       for (std::vector<base::FilePath>::const_iterator it = paths.begin();
    297            it != paths.end(); ++it) {
    298         DCHECK(drive::util::IsUnderDriveMountPoint(*it));
    299         drive::util::PrepareWritableFileAndRun(
    300             profile,
    301             *it,
    302             base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this));
    303       }
    304       return;
    305     }
    306 #endif
    307     content::BrowserThread::PostTask(
    308         content::BrowserThread::FILE,
    309         FROM_HERE,
    310         base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this, paths));
    311   }
    312 
    313  private:
    314   friend class base::RefCountedThreadSafe<WritableFileChecker>;
    315   virtual ~WritableFileChecker() {}
    316 
    317   // Called when a work item is completed. If all work items are done, this
    318   // calls the success or failure callback.
    319   void TaskDone() {
    320     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    321     if (--outstanding_tasks_ == 0) {
    322       if (error_.empty())
    323         on_success_.Run();
    324       else
    325         on_failure_.Run(error_);
    326     }
    327   }
    328 
    329   // Reports an error in completing a work item. This may be called more than
    330   // once, but only the last message will be retained.
    331   void Error(const std::string& message) {
    332     DCHECK(!message.empty());
    333     error_ = message;
    334     TaskDone();
    335   }
    336 
    337   void CheckLocalWritableFiles(const std::vector<base::FilePath>& paths) {
    338     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    339     std::string error;
    340     for (std::vector<base::FilePath>::const_iterator it = paths.begin();
    341          it != paths.end(); ++it) {
    342       if (!DoCheckWritableFile(*it, extension_path_, &error)) {
    343         content::BrowserThread::PostTask(
    344             content::BrowserThread::UI,
    345             FROM_HERE,
    346             base::Bind(&WritableFileChecker::Error, this, error));
    347         return;
    348       }
    349     }
    350     content::BrowserThread::PostTask(
    351         content::BrowserThread::UI,
    352         FROM_HERE,
    353         base::Bind(&WritableFileChecker::TaskDone, this));
    354   }
    355 
    356 #if defined(OS_CHROMEOS)
    357   void CheckRemoteWritableFile(drive::FileError error,
    358                                const base::FilePath& path) {
    359     if (error == drive::FILE_ERROR_OK) {
    360       content::BrowserThread::PostTask(
    361           content::BrowserThread::UI,
    362           FROM_HERE,
    363           base::Bind(&WritableFileChecker::TaskDone, this));
    364     } else {
    365       content::BrowserThread::PostTask(
    366           content::BrowserThread::UI,
    367           FROM_HERE,
    368           base::Bind(
    369               &WritableFileChecker::Error,
    370               this,
    371               base::StringPrintf(kWritableFileErrorFormat,
    372                                  path.BaseName().AsUTF8Unsafe().c_str())));
    373     }
    374   }
    375 #endif
    376 
    377   int outstanding_tasks_;
    378   const base::FilePath extension_path_;
    379   std::string error_;
    380   base::Closure on_success_;
    381   base::Callback<void(const std::string&)> on_failure_;
    382 };
    383 
    384 // Expand the mime-types and extensions provided in an AcceptOption, returning
    385 // them within the passed extension vector. Returns false if no valid types
    386 // were found.
    387 bool GetFileTypesFromAcceptOption(
    388     const file_system::AcceptOption& accept_option,
    389     std::vector<base::FilePath::StringType>* extensions,
    390     string16* description) {
    391   std::set<base::FilePath::StringType> extension_set;
    392   int description_id = 0;
    393 
    394   if (accept_option.mime_types.get()) {
    395     std::vector<std::string>* list = accept_option.mime_types.get();
    396     bool valid_type = false;
    397     for (std::vector<std::string>::const_iterator iter = list->begin();
    398          iter != list->end(); ++iter) {
    399       std::vector<base::FilePath::StringType> inner;
    400       std::string accept_type = *iter;
    401       StringToLowerASCII(&accept_type);
    402       net::GetExtensionsForMimeType(accept_type, &inner);
    403       if (inner.empty())
    404         continue;
    405 
    406       if (valid_type)
    407         description_id = 0;  // We already have an accept type with label; if
    408                              // we find another, give up and use the default.
    409       else if (accept_type == "image/*")
    410         description_id = IDS_IMAGE_FILES;
    411       else if (accept_type == "audio/*")
    412         description_id = IDS_AUDIO_FILES;
    413       else if (accept_type == "video/*")
    414         description_id = IDS_VIDEO_FILES;
    415 
    416       extension_set.insert(inner.begin(), inner.end());
    417       valid_type = true;
    418     }
    419   }
    420 
    421   if (accept_option.extensions.get()) {
    422     std::vector<std::string>* list = accept_option.extensions.get();
    423     for (std::vector<std::string>::const_iterator iter = list->begin();
    424          iter != list->end(); ++iter) {
    425       std::string extension = *iter;
    426       StringToLowerASCII(&extension);
    427 #if defined(OS_WIN)
    428       extension_set.insert(UTF8ToWide(*iter));
    429 #else
    430       extension_set.insert(*iter);
    431 #endif
    432     }
    433   }
    434 
    435   extensions->assign(extension_set.begin(), extension_set.end());
    436   if (extensions->empty())
    437     return false;
    438 
    439   if (accept_option.description.get())
    440     *description = UTF8ToUTF16(*accept_option.description.get());
    441   else if (description_id)
    442     *description = l10n_util::GetStringUTF16(description_id);
    443 
    444   return true;
    445 }
    446 
    447 // Key for the path of the directory of the file last chosen by the user in
    448 // response to a chrome.fileSystem.chooseEntry() call.
    449 const char kLastChooseEntryDirectory[] = "last_choose_file_directory";
    450 
    451 }  // namespace
    452 
    453 namespace extensions {
    454 
    455 namespace file_system_api {
    456 
    457 bool GetLastChooseEntryDirectory(const ExtensionPrefs* prefs,
    458                                  const std::string& extension_id,
    459                                  base::FilePath* path) {
    460   std::string string_path;
    461   if (!prefs->ReadPrefAsString(extension_id,
    462                                kLastChooseEntryDirectory,
    463                                &string_path)) {
    464     return false;
    465   }
    466 
    467   *path = base::FilePath::FromUTF8Unsafe(string_path);
    468   return true;
    469 }
    470 
    471 void SetLastChooseEntryDirectory(ExtensionPrefs* prefs,
    472                                  const std::string& extension_id,
    473                                  const base::FilePath& path) {
    474   prefs->UpdateExtensionPref(extension_id,
    475                              kLastChooseEntryDirectory,
    476                              base::CreateFilePathValue(path));
    477 }
    478 
    479 }  // namespace file_system_api
    480 
    481 bool FileSystemGetDisplayPathFunction::RunImpl() {
    482   std::string filesystem_name;
    483   std::string filesystem_path;
    484   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
    485   EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
    486 
    487   base::FilePath file_path;
    488   if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path,
    489                               render_view_host_, &file_path, &error_))
    490     return false;
    491 
    492   file_path = PrettifyPath(file_path);
    493   SetResult(base::Value::CreateStringValue(file_path.value()));
    494   return true;
    495 }
    496 
    497 FileSystemEntryFunction::FileSystemEntryFunction()
    498     : multiple_(false),
    499       entry_type_(READ_ONLY),
    500       response_(NULL) {}
    501 
    502 bool FileSystemEntryFunction::HasFileSystemWritePermission() {
    503   const extensions::Extension* extension = GetExtension();
    504   if (!extension)
    505     return false;
    506 
    507   return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
    508 }
    509 
    510 void FileSystemEntryFunction::CheckWritableFiles(
    511     const std::vector<base::FilePath>& paths) {
    512   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    513   scoped_refptr<WritableFileChecker> helper = new WritableFileChecker(
    514       paths, profile_, extension_->path(),
    515       base::Bind(
    516           &FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
    517           this, paths),
    518       base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
    519 }
    520 
    521 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
    522     const std::vector<base::FilePath>& paths) {
    523   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    524 
    525   CreateResponse();
    526   for (std::vector<base::FilePath>::const_iterator it = paths.begin();
    527        it != paths.end(); ++it) {
    528     AddEntryToResponse(*it, "");
    529   }
    530   SendResponse(true);
    531 }
    532 
    533 void FileSystemEntryFunction::CreateResponse() {
    534   DCHECK(!response_);
    535   response_ = new base::DictionaryValue();
    536   base::ListValue* list = new base::ListValue();
    537   response_->Set("entries", list);
    538   response_->SetBoolean("multiple", multiple_);
    539   SetResult(response_);
    540 }
    541 
    542 void FileSystemEntryFunction::AddEntryToResponse(
    543     const base::FilePath& path,
    544     const std::string& id_override) {
    545   DCHECK(response_);
    546   bool writable = entry_type_ == WRITABLE;
    547   extensions::app_file_handler_util::GrantedFileEntry file_entry =
    548       extensions::app_file_handler_util::CreateFileEntry(
    549           profile(),
    550           GetExtension()->id(),
    551           render_view_host_->GetProcess()->GetID(),
    552           path,
    553           writable);
    554   base::ListValue* entries;
    555   bool success = response_->GetList("entries", &entries);
    556   DCHECK(success);
    557 
    558   base::DictionaryValue* entry = new base::DictionaryValue();
    559   entry->SetString("fileSystemId", file_entry.filesystem_id);
    560   entry->SetString("baseName", file_entry.registered_name);
    561   if (id_override.empty())
    562     entry->SetString("id", file_entry.id);
    563   else
    564     entry->SetString("id", id_override);
    565   entries->Append(entry);
    566 }
    567 
    568 void FileSystemEntryFunction::HandleWritableFileError(
    569     const std::string& error) {
    570   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    571   error_ = error;
    572   SendResponse(false);
    573 }
    574 
    575 bool FileSystemGetWritableEntryFunction::RunImpl() {
    576   std::string filesystem_name;
    577   std::string filesystem_path;
    578   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
    579   EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
    580 
    581   if (!HasFileSystemWritePermission()) {
    582     error_ = kRequiresFileSystemWriteError;
    583     return false;
    584   }
    585   entry_type_ = WRITABLE;
    586 
    587   base::FilePath path;
    588   if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path,
    589                               render_view_host_, &path, &error_))
    590     return false;
    591 
    592   std::vector<base::FilePath> paths;
    593   paths.push_back(path);
    594   CheckWritableFiles(paths);
    595   return true;
    596 }
    597 
    598 bool FileSystemIsWritableEntryFunction::RunImpl() {
    599   std::string filesystem_name;
    600   std::string filesystem_path;
    601   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
    602   EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
    603 
    604   std::string filesystem_id;
    605   if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
    606     error_ = kInvalidParameters;
    607     return false;
    608   }
    609 
    610   content::ChildProcessSecurityPolicy* policy =
    611       content::ChildProcessSecurityPolicy::GetInstance();
    612   int renderer_id = render_view_host_->GetProcess()->GetID();
    613   bool is_writable = policy->CanReadWriteFileSystem(renderer_id,
    614                                                     filesystem_id);
    615 
    616   SetResult(base::Value::CreateBooleanValue(is_writable));
    617   return true;
    618 }
    619 
    620 // Handles showing a dialog to the user to ask for the filename for a file to
    621 // save or open.
    622 class FileSystemChooseEntryFunction::FilePicker
    623     : public ui::SelectFileDialog::Listener {
    624  public:
    625   FilePicker(FileSystemChooseEntryFunction* function,
    626              content::WebContents* web_contents,
    627              const base::FilePath& suggested_name,
    628              const ui::SelectFileDialog::FileTypeInfo& file_type_info,
    629              ui::SelectFileDialog::Type picker_type)
    630       : function_(function) {
    631     select_file_dialog_ = ui::SelectFileDialog::Create(
    632         this, new ChromeSelectFilePolicy(web_contents));
    633     gfx::NativeWindow owning_window = web_contents ?
    634         platform_util::GetTopLevel(web_contents->GetView()->GetNativeView()) :
    635         NULL;
    636 
    637     if (g_skip_picker_for_test) {
    638       if (g_use_suggested_path_for_test) {
    639         content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
    640             base::Bind(
    641                 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
    642                 base::Unretained(this), suggested_name, 1,
    643                 static_cast<void*>(NULL)));
    644       } else if (g_path_to_be_picked_for_test) {
    645         content::BrowserThread::PostTask(
    646             content::BrowserThread::UI, FROM_HERE,
    647             base::Bind(
    648                 &FileSystemChooseEntryFunction::FilePicker::FileSelected,
    649                 base::Unretained(this), *g_path_to_be_picked_for_test, 1,
    650                 static_cast<void*>(NULL)));
    651       } else if (g_paths_to_be_picked_for_test) {
    652         content::BrowserThread::PostTask(
    653             content::BrowserThread::UI,
    654             FROM_HERE,
    655             base::Bind(
    656                 &FileSystemChooseEntryFunction::FilePicker::MultiFilesSelected,
    657                 base::Unretained(this),
    658                 *g_paths_to_be_picked_for_test,
    659                 static_cast<void*>(NULL)));
    660       } else {
    661         content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
    662             base::Bind(
    663                 &FileSystemChooseEntryFunction::FilePicker::
    664                     FileSelectionCanceled,
    665                 base::Unretained(this), static_cast<void*>(NULL)));
    666       }
    667       return;
    668     }
    669 
    670     select_file_dialog_->SelectFile(picker_type,
    671                                     string16(),
    672                                     suggested_name,
    673                                     &file_type_info,
    674                                     0,
    675                                     base::FilePath::StringType(),
    676                                     owning_window,
    677                                     NULL);
    678   }
    679 
    680   virtual ~FilePicker() {}
    681 
    682  private:
    683   // ui::SelectFileDialog::Listener implementation.
    684   virtual void FileSelected(const base::FilePath& path,
    685                             int index,
    686                             void* params) OVERRIDE {
    687     std::vector<base::FilePath> paths;
    688     paths.push_back(path);
    689     MultiFilesSelected(paths, params);
    690   }
    691 
    692   virtual void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
    693                                          int index,
    694                                          void* params) OVERRIDE {
    695     // Normally, file.local_path is used because it is a native path to the
    696     // local read-only cached file in the case of remote file system like
    697     // Chrome OS's Google Drive integration. Here, however, |file.file_path| is
    698     // necessary because we need to create a FileEntry denoting the remote file,
    699     // not its cache. On other platforms than Chrome OS, they are the same.
    700     //
    701     // TODO(kinaba): remove this, once after the file picker implements proper
    702     // switch of the path treatment depending on the |support_drive| flag.
    703     FileSelected(file.file_path, index, params);
    704   }
    705 
    706   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
    707                                   void* params) OVERRIDE {
    708     function_->FilesSelected(files);
    709     delete this;
    710   }
    711 
    712   virtual void MultiFilesSelectedWithExtraInfo(
    713       const std::vector<ui::SelectedFileInfo>& files,
    714       void* params) OVERRIDE {
    715     std::vector<base::FilePath> paths;
    716     for (std::vector<ui::SelectedFileInfo>::const_iterator it = files.begin();
    717          it != files.end(); ++it) {
    718       paths.push_back(it->file_path);
    719     }
    720     MultiFilesSelected(paths, params);
    721   }
    722 
    723   virtual void FileSelectionCanceled(void* params) OVERRIDE {
    724     function_->FileSelectionCanceled();
    725     delete this;
    726   }
    727 
    728   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
    729   scoped_refptr<FileSystemChooseEntryFunction> function_;
    730 
    731   DISALLOW_COPY_AND_ASSIGN(FilePicker);
    732 };
    733 
    734 void FileSystemChooseEntryFunction::ShowPicker(
    735     const ui::SelectFileDialog::FileTypeInfo& file_type_info,
    736     ui::SelectFileDialog::Type picker_type) {
    737   // TODO(asargent/benwells) - As a short term remediation for crbug.com/179010
    738   // we're adding the ability for a whitelisted extension to use this API since
    739   // chrome.fileBrowserHandler.selectFile is ChromeOS-only. Eventually we'd
    740   // like a better solution and likely this code will go back to being
    741   // platform-app only.
    742   content::WebContents* web_contents = NULL;
    743   if (extension_->is_platform_app()) {
    744     apps::ShellWindowRegistry* registry =
    745         apps::ShellWindowRegistry::Get(profile());
    746     DCHECK(registry);
    747     ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost(
    748         render_view_host());
    749     if (!shell_window) {
    750       error_ = kInvalidCallingPage;
    751       SendResponse(false);
    752       return;
    753     }
    754     web_contents = shell_window->web_contents();
    755   } else {
    756     web_contents = GetAssociatedWebContents();
    757   }
    758   // The file picker will hold a reference to this function instance, preventing
    759   // its destruction (and subsequent sending of the function response) until the
    760   // user has selected a file or cancelled the picker. At that point, the picker
    761   // will delete itself, which will also free the function instance.
    762   new FilePicker(
    763       this, web_contents, initial_path_, file_type_info, picker_type);
    764 }
    765 
    766 // static
    767 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
    768     base::FilePath* path) {
    769   g_skip_picker_for_test = true;
    770   g_use_suggested_path_for_test = false;
    771   g_path_to_be_picked_for_test = path;
    772   g_paths_to_be_picked_for_test = NULL;
    773 }
    774 
    775 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
    776     std::vector<base::FilePath>* paths) {
    777   g_skip_picker_for_test = true;
    778   g_use_suggested_path_for_test = false;
    779   g_paths_to_be_picked_for_test = paths;
    780 }
    781 
    782 // static
    783 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() {
    784   g_skip_picker_for_test = true;
    785   g_use_suggested_path_for_test = true;
    786   g_path_to_be_picked_for_test = NULL;
    787   g_paths_to_be_picked_for_test = NULL;
    788 }
    789 
    790 // static
    791 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() {
    792   g_skip_picker_for_test = true;
    793   g_use_suggested_path_for_test = false;
    794   g_path_to_be_picked_for_test = NULL;
    795   g_paths_to_be_picked_for_test = NULL;
    796 }
    797 
    798 // static
    799 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
    800   g_skip_picker_for_test = false;
    801 }
    802 
    803 // static
    804 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
    805     const std::string& name, const base::FilePath& path) {
    806   // For testing on Chrome OS, where to deal with remote and local paths
    807   // smoothly, all accessed paths need to be registered in the list of
    808   // external mount points.
    809   fileapi::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
    810       name, fileapi::kFileSystemTypeNativeLocal, path);
    811 }
    812 
    813 void FileSystemChooseEntryFunction::SetInitialPathOnFileThread(
    814     const base::FilePath& suggested_name,
    815     const base::FilePath& previous_path) {
    816   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    817   if (!previous_path.empty() && base::DirectoryExists(previous_path)) {
    818     initial_path_ = previous_path.Append(suggested_name);
    819   } else {
    820     base::FilePath documents_dir;
    821     if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) {
    822       initial_path_ = documents_dir.Append(suggested_name);
    823     } else {
    824       initial_path_ = suggested_name;
    825     }
    826   }
    827 }
    828 
    829 void FileSystemChooseEntryFunction::FilesSelected(
    830     const std::vector<base::FilePath>& paths) {
    831   DCHECK(!paths.empty());
    832   file_system_api::SetLastChooseEntryDirectory(
    833       ExtensionPrefs::Get(profile()), GetExtension()->id(), paths[0].DirName());
    834   if (entry_type_ == WRITABLE) {
    835     CheckWritableFiles(paths);
    836     return;
    837   }
    838 
    839   // Don't need to check the file, it's for reading.
    840   RegisterFileSystemsAndSendResponse(paths);
    841 }
    842 
    843 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
    844   error_ = kUserCancelled;
    845   SendResponse(false);
    846 }
    847 
    848 void FileSystemChooseEntryFunction::BuildFileTypeInfo(
    849     ui::SelectFileDialog::FileTypeInfo* file_type_info,
    850     const base::FilePath::StringType& suggested_extension,
    851     const AcceptOptions* accepts,
    852     const bool* acceptsAllTypes) {
    853   file_type_info->include_all_files = true;
    854   if (acceptsAllTypes)
    855     file_type_info->include_all_files = *acceptsAllTypes;
    856 
    857   bool need_suggestion = !file_type_info->include_all_files &&
    858                          !suggested_extension.empty();
    859 
    860   if (accepts) {
    861     typedef file_system::AcceptOption AcceptOption;
    862     for (std::vector<linked_ptr<AcceptOption> >::const_iterator iter =
    863             accepts->begin(); iter != accepts->end(); ++iter) {
    864       string16 description;
    865       std::vector<base::FilePath::StringType> extensions;
    866 
    867       if (!GetFileTypesFromAcceptOption(**iter, &extensions, &description))
    868         continue;  // No extensions were found.
    869 
    870       file_type_info->extensions.push_back(extensions);
    871       file_type_info->extension_description_overrides.push_back(description);
    872 
    873       // If we still need to find suggested_extension, hunt for it inside the
    874       // extensions returned from GetFileTypesFromAcceptOption.
    875       if (need_suggestion && std::find(extensions.begin(),
    876               extensions.end(), suggested_extension) != extensions.end()) {
    877         need_suggestion = false;
    878       }
    879     }
    880   }
    881 
    882   // If there's nothing in our accepted extension list or we couldn't find the
    883   // suggested extension required, then default to accepting all types.
    884   if (file_type_info->extensions.empty() || need_suggestion)
    885     file_type_info->include_all_files = true;
    886 }
    887 
    888 void FileSystemChooseEntryFunction::BuildSuggestion(
    889     const std::string *opt_name,
    890     base::FilePath* suggested_name,
    891     base::FilePath::StringType* suggested_extension) {
    892   if (opt_name) {
    893     *suggested_name = base::FilePath::FromUTF8Unsafe(*opt_name);
    894 
    895     // Don't allow any path components; shorten to the base name. This should
    896     // result in a relative path, but in some cases may not. Clear the
    897     // suggestion for safety if this is the case.
    898     *suggested_name = suggested_name->BaseName();
    899     if (suggested_name->IsAbsolute())
    900       *suggested_name = base::FilePath();
    901 
    902     *suggested_extension = suggested_name->Extension();
    903     if (!suggested_extension->empty())
    904       suggested_extension->erase(suggested_extension->begin());  // drop the .
    905   }
    906 }
    907 
    908 bool FileSystemChooseEntryFunction::RunImpl() {
    909   scoped_ptr<ChooseEntry::Params> params(ChooseEntry::Params::Create(*args_));
    910   EXTENSION_FUNCTION_VALIDATE(params.get());
    911 
    912   base::FilePath suggested_name;
    913   ui::SelectFileDialog::FileTypeInfo file_type_info;
    914   ui::SelectFileDialog::Type picker_type =
    915       ui::SelectFileDialog::SELECT_OPEN_FILE;
    916 
    917   file_system::ChooseEntryOptions* options = params->options.get();
    918   if (options) {
    919     multiple_ = options->accepts_multiple;
    920     if (multiple_)
    921       picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
    922     if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) {
    923       entry_type_ = WRITABLE;
    924     } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
    925       if (multiple_) {
    926         error_ = kMultipleUnsupportedError;
    927         return false;
    928       }
    929       entry_type_ = WRITABLE;
    930       picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
    931     }
    932 
    933     base::FilePath::StringType suggested_extension;
    934     BuildSuggestion(options->suggested_name.get(), &suggested_name,
    935         &suggested_extension);
    936 
    937     BuildFileTypeInfo(&file_type_info, suggested_extension,
    938         options->accepts.get(), options->accepts_all_types.get());
    939   }
    940 
    941   if (entry_type_ == WRITABLE && !HasFileSystemWritePermission()) {
    942     error_ = kRequiresFileSystemWriteError;
    943     return false;
    944   }
    945 
    946   file_type_info.support_drive = true;
    947 
    948   base::FilePath previous_path;
    949   file_system_api::GetLastChooseEntryDirectory(
    950       ExtensionPrefs::Get(profile()),
    951       GetExtension()->id(),
    952       &previous_path);
    953 
    954   content::BrowserThread::PostTaskAndReply(
    955       content::BrowserThread::FILE,
    956       FROM_HERE,
    957       base::Bind(
    958           &FileSystemChooseEntryFunction::SetInitialPathOnFileThread, this,
    959           suggested_name, previous_path),
    960       base::Bind(
    961           &FileSystemChooseEntryFunction::ShowPicker, this, file_type_info,
    962           picker_type));
    963   return true;
    964 }
    965 
    966 bool FileSystemRetainEntryFunction::RunImpl() {
    967   std::string entry_id;
    968   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
    969   SavedFilesService* saved_files_service = SavedFilesService::Get(profile());
    970   // Add the file to the retain list if it is not already on there.
    971   if (!saved_files_service->IsRegistered(extension_->id(), entry_id) &&
    972       !RetainFileEntry(entry_id)) {
    973     return false;
    974   }
    975   saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
    976   return true;
    977 }
    978 
    979 bool FileSystemRetainEntryFunction::RetainFileEntry(
    980     const std::string& entry_id) {
    981   std::string filesystem_name;
    982   std::string filesystem_path;
    983   EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
    984   EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
    985   std::string filesystem_id;
    986   base::FilePath path;
    987   if (!GetFileSystemAndPathOfFileEntry(filesystem_name,
    988                                        filesystem_path,
    989                                        render_view_host_,
    990                                        &filesystem_id,
    991                                        &path,
    992                                        &error_)) {
    993     return false;
    994   }
    995 
    996   content::ChildProcessSecurityPolicy* policy =
    997       content::ChildProcessSecurityPolicy::GetInstance();
    998   bool is_writable = policy->CanReadWriteFileSystem(
    999       render_view_host_->GetProcess()->GetID(), filesystem_id);
   1000   SavedFilesService::Get(profile())->RegisterFileEntry(
   1001       extension_->id(), entry_id, path, is_writable);
   1002   return true;
   1003 }
   1004 
   1005 bool FileSystemIsRestorableFunction::RunImpl() {
   1006   std::string entry_id;
   1007   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
   1008   SetResult(new base::FundamentalValue(SavedFilesService::Get(
   1009       profile())->IsRegistered(extension_->id(), entry_id)));
   1010   return true;
   1011 }
   1012 
   1013 bool FileSystemRestoreEntryFunction::RunImpl() {
   1014   std::string entry_id;
   1015   bool needs_new_entry;
   1016   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
   1017   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &needs_new_entry));
   1018   const SavedFileEntry* file_entry = SavedFilesService::Get(
   1019       profile())->GetFileEntry(extension_->id(), entry_id);
   1020   if (!file_entry) {
   1021     error_ = kUnknownIdError;
   1022     return false;
   1023   }
   1024 
   1025   SavedFilesService::Get(profile())->EnqueueFileEntry(
   1026       extension_->id(), entry_id);
   1027 
   1028   // Only create a new file entry if the renderer requests one.
   1029   // |needs_new_entry| will be false if the renderer already has an Entry for
   1030   // |entry_id|.
   1031   if (needs_new_entry) {
   1032     entry_type_ = file_entry->writable ? WRITABLE : READ_ONLY;
   1033     CreateResponse();
   1034     AddEntryToResponse(file_entry->path, file_entry->id);
   1035   }
   1036   SendResponse(true);
   1037   return true;
   1038 }
   1039 
   1040 }  // namespace extensions
   1041