Home | History | Annotate | Download | only in file_manager
      1 // Copyright 2013 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/chromeos/extensions/file_manager/private_api_tasks.h"
      6 
      7 #include <set>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "chrome/browser/chromeos/drive/file_system_util.h"
     12 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
     13 #include "chrome/browser/chromeos/file_manager/mime_util.h"
     14 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "net/base/filename_util.h"
     18 #include "net/base/mime_sniffer.h"
     19 #include "webkit/browser/fileapi/file_system_context.h"
     20 #include "webkit/browser/fileapi/file_system_url.h"
     21 
     22 using content::BrowserThread;
     23 using extensions::app_file_handler_util::PathAndMimeTypeSet;
     24 using fileapi::FileSystemURL;
     25 
     26 namespace extensions {
     27 namespace {
     28 
     29 // Error messages.
     30 const char kInvalidTask[] = "Invalid task: ";
     31 const char kInvalidFileUrl[] = "Invalid file URL";
     32 
     33 // Make a set of unique filename suffixes out of the list of file URLs.
     34 std::set<std::string> GetUniqueSuffixes(
     35     const std::vector<std::string>& file_url_list,
     36     const fileapi::FileSystemContext* context) {
     37   std::set<std::string> suffixes;
     38   for (size_t i = 0; i < file_url_list.size(); ++i) {
     39     const FileSystemURL url = context->CrackURL(GURL(file_url_list[i]));
     40     if (!url.is_valid() || url.path().empty())
     41       return std::set<std::string>();
     42     // We'll skip empty suffixes.
     43     if (!url.path().Extension().empty())
     44       suffixes.insert(url.path().Extension());
     45   }
     46   return suffixes;
     47 }
     48 
     49 // Make a set of unique MIME types out of the list of MIME types.
     50 std::set<std::string> GetUniqueMimeTypes(
     51     const std::vector<std::string>& mime_type_list) {
     52   std::set<std::string> mime_types;
     53   for (size_t i = 0; i < mime_type_list.size(); ++i) {
     54     const std::string mime_type = mime_type_list[i];
     55     // We'll skip empty MIME types and existing MIME types.
     56     if (!mime_type.empty())
     57       mime_types.insert(mime_type);
     58   }
     59   return mime_types;
     60 }
     61 
     62 void SniffMimeType(PathAndMimeTypeSet* path_mime_set,
     63                    std::vector<GURL>* file_urls) {
     64   PathAndMimeTypeSet sniffed_path_mime_set;
     65   std::vector<char> content(net::kMaxBytesToSniff);
     66 
     67   // For each files, sniff its MIME type if it is empty
     68   for (PathAndMimeTypeSet::iterator it = path_mime_set->begin();
     69        it != path_mime_set->end();
     70        ++it) {
     71     const base::FilePath& file_path = it->first;
     72     std::string mime_type = it->second;
     73     // Note: sniff MIME type only for local files.
     74     if (mime_type.empty() && !drive::util::IsUnderDriveMountPoint(file_path)) {
     75       int bytes_read = base::ReadFile(file_path, &content[0], content.size());
     76       if (bytes_read >= 0) {
     77         net::SniffMimeType(&content[0],
     78                            bytes_read,
     79                            net::FilePathToFileURL(file_path),
     80                            std::string(),  // type_hint (passes no hint)
     81                            &mime_type);
     82       }
     83     }
     84     sniffed_path_mime_set.insert(std::make_pair(file_path, mime_type));
     85   }
     86   path_mime_set->swap(sniffed_path_mime_set);
     87 }
     88 
     89 }  // namespace
     90 
     91 bool FileBrowserPrivateExecuteTaskFunction::RunAsync() {
     92   using extensions::api::file_browser_private::ExecuteTask::Params;
     93   using extensions::api::file_browser_private::ExecuteTask::Results::Create;
     94   const scoped_ptr<Params> params(Params::Create(*args_));
     95   EXTENSION_FUNCTION_VALIDATE(params);
     96 
     97   file_manager::file_tasks::TaskDescriptor task;
     98   if (!file_manager::file_tasks::ParseTaskID(params->task_id, &task)) {
     99     SetError(kInvalidTask + params->task_id);
    100     results_ =
    101         Create(extensions::api::file_browser_private::TASK_RESULT_FAILED);
    102     return false;
    103   }
    104 
    105   if (params->file_urls.empty()) {
    106     results_ = Create(extensions::api::file_browser_private::TASK_RESULT_EMPTY);
    107     SendResponse(true);
    108     return true;
    109   }
    110 
    111   const scoped_refptr<fileapi::FileSystemContext> file_system_context =
    112       file_manager::util::GetFileSystemContextForRenderViewHost(
    113           GetProfile(), render_view_host());
    114 
    115   std::vector<FileSystemURL> file_urls;
    116   for (size_t i = 0; i < params->file_urls.size(); i++) {
    117     const FileSystemURL url =
    118         file_system_context->CrackURL(GURL(params->file_urls[i]));
    119     if (!chromeos::FileSystemBackend::CanHandleURL(url)) {
    120       SetError(kInvalidFileUrl);
    121       results_ =
    122           Create(extensions::api::file_browser_private::TASK_RESULT_FAILED);
    123       return false;
    124     }
    125     file_urls.push_back(url);
    126   }
    127 
    128   const bool result = file_manager::file_tasks::ExecuteFileTask(
    129       GetProfile(),
    130       source_url(),
    131       task,
    132       file_urls,
    133       base::Bind(&FileBrowserPrivateExecuteTaskFunction::OnTaskExecuted, this));
    134   if (!result) {
    135     results_ =
    136         Create(extensions::api::file_browser_private::TASK_RESULT_FAILED);
    137   }
    138   return result;
    139 }
    140 
    141 void FileBrowserPrivateExecuteTaskFunction::OnTaskExecuted(
    142     extensions::api::file_browser_private::TaskResult result) {
    143   results_ =
    144       extensions::api::file_browser_private::ExecuteTask::Results::Create(
    145           result);
    146   SendResponse(result !=
    147                extensions::api::file_browser_private::TASK_RESULT_FAILED);
    148 }
    149 
    150 bool FileBrowserPrivateGetFileTasksFunction::RunAsync() {
    151   using extensions::api::file_browser_private::GetFileTasks::Params;
    152   const scoped_ptr<Params> params(Params::Create(*args_));
    153   EXTENSION_FUNCTION_VALIDATE(params);
    154 
    155   if (params->file_urls.empty())
    156     return false;
    157 
    158   // MIME types can either be empty, or there needs to be one for each file.
    159   if (params->mime_types.size() != params->file_urls.size() &&
    160       params->mime_types.size() != 0)
    161     return false;
    162 
    163   const scoped_refptr<fileapi::FileSystemContext> file_system_context =
    164       file_manager::util::GetFileSystemContextForRenderViewHost(
    165           GetProfile(), render_view_host());
    166 
    167   // Collect all the URLs, convert them to GURLs, and crack all the urls into
    168   // file paths.
    169   scoped_ptr<PathAndMimeTypeSet> path_mime_set(new PathAndMimeTypeSet);
    170   scoped_ptr<std::vector<GURL> > file_urls(new std::vector<GURL>);
    171   for (size_t i = 0; i < params->file_urls.size(); ++i) {
    172     std::string mime_type;
    173     if (params->mime_types.size() != 0)
    174       mime_type = params->mime_types[i];
    175 
    176     const GURL file_url(params->file_urls[i]);
    177     fileapi::FileSystemURL file_system_url(
    178         file_system_context->CrackURL(file_url));
    179     if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
    180       continue;
    181     const base::FilePath file_path = file_system_url.path();
    182 
    183     file_urls->push_back(file_url);
    184 
    185     // If MIME type is not provided, guess it from the file path.
    186     if (mime_type.empty())
    187       mime_type = file_manager::util::GetMimeTypeForPath(file_path);
    188 
    189     path_mime_set->insert(std::make_pair(file_path, mime_type));
    190   }
    191 
    192   // In case the MIME type of some files are empty,
    193   // try to sniff their MIME type by their content.
    194   PathAndMimeTypeSet* path_mime_set_ptr = path_mime_set.get();
    195   std::vector<GURL>* file_urls_ptr = file_urls.get();
    196 
    197   BrowserThread::PostBlockingPoolTaskAndReply(
    198       FROM_HERE,
    199       base::Bind(&SniffMimeType, path_mime_set_ptr, file_urls_ptr),
    200       base::Bind(
    201           &FileBrowserPrivateGetFileTasksFunction::OnSniffingMimeTypeCompleted,
    202           this,
    203           base::Passed(&path_mime_set),
    204           base::Passed(&file_urls)));
    205   return true;
    206 }
    207 
    208 void FileBrowserPrivateGetFileTasksFunction::OnSniffingMimeTypeCompleted(
    209     scoped_ptr<PathAndMimeTypeSet> path_mime_set,
    210     scoped_ptr<std::vector<GURL> > file_urls) {
    211   std::vector<file_manager::file_tasks::FullTaskDescriptor> tasks;
    212   file_manager::file_tasks::FindAllTypesOfTasks(
    213       GetProfile(),
    214       drive::util::GetDriveAppRegistryByProfile(GetProfile()),
    215       *path_mime_set,
    216       *file_urls,
    217       &tasks);
    218 
    219   // Convert the tasks into JSON compatible objects.
    220   using api::file_browser_private::FileTask;
    221   std::vector<linked_ptr<FileTask> > results;
    222   for (size_t i = 0; i < tasks.size(); ++i) {
    223     const file_manager::file_tasks::FullTaskDescriptor& task = tasks[i];
    224     const linked_ptr<FileTask> converted(new FileTask);
    225     converted->task_id = file_manager::file_tasks::TaskDescriptorToId(
    226         task.task_descriptor());
    227     if (!task.icon_url().is_empty())
    228       converted->icon_url = task.icon_url().spec();
    229     converted->title = task.task_title();
    230     converted->is_default = task.is_default();
    231     results.push_back(converted);
    232   }
    233   results_ = extensions::api::file_browser_private::GetFileTasks::Results::
    234       Create(results);
    235   SendResponse(true);
    236 }
    237 
    238 bool FileBrowserPrivateSetDefaultTaskFunction::RunSync() {
    239   using extensions::api::file_browser_private::SetDefaultTask::Params;
    240   const scoped_ptr<Params> params(Params::Create(*args_));
    241   EXTENSION_FUNCTION_VALIDATE(params);
    242 
    243   const scoped_refptr<fileapi::FileSystemContext> file_system_context =
    244       file_manager::util::GetFileSystemContextForRenderViewHost(
    245           GetProfile(), render_view_host());
    246 
    247   const std::set<std::string> suffixes =
    248       GetUniqueSuffixes(params->file_urls, file_system_context.get());
    249 
    250   // MIME types are an optional parameter.
    251   std::set<std::string> mime_types;
    252   if (params->mime_types && !params->mime_types->empty()) {
    253     if (params->mime_types->size() != params->file_urls.size())
    254       return false;
    255     mime_types = GetUniqueMimeTypes(*params->mime_types);
    256   }
    257 
    258   // If there weren't any mime_types, and all the suffixes were blank,
    259   // then we "succeed", but don't actually associate with anything.
    260   // Otherwise, any time we set the default on a file with no extension
    261   // on the local drive, we'd fail.
    262   // TODO(gspencer): Fix file manager so that it never tries to set default in
    263   // cases where extensionless local files are part of the selection.
    264   if (suffixes.empty() && mime_types.empty()) {
    265     SetResult(new base::FundamentalValue(true));
    266     return true;
    267   }
    268 
    269   file_manager::file_tasks::UpdateDefaultTask(
    270       GetProfile()->GetPrefs(), params->task_id, suffixes, mime_types);
    271   return true;
    272 }
    273 
    274 }  // namespace extensions
    275