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