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/chromeos/file_manager/file_browser_handlers.h" 6 7 #include "base/bind.h" 8 #include "base/file_util.h" 9 #include "base/i18n/case_conversion.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "chrome/browser/chromeos/drive/file_system_util.h" 12 #include "chrome/browser/chromeos/file_manager/app_id.h" 13 #include "chrome/browser/chromeos/file_manager/fileapi_util.h" 14 #include "chrome/browser/chromeos/file_manager/open_with_browser.h" 15 #include "chrome/browser/chromeos/fileapi/file_system_backend.h" 16 #include "chrome/browser/extensions/extension_service.h" 17 #include "chrome/browser/extensions/extension_util.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/ui/browser_finder.h" 20 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h" 21 #include "chrome/common/extensions/api/file_browser_private.h" 22 #include "chrome/common/extensions/extension_constants.h" 23 #include "content/public/browser/browser_thread.h" 24 #include "content/public/browser/child_process_security_policy.h" 25 #include "content/public/browser/render_process_host.h" 26 #include "content/public/browser/site_instance.h" 27 #include "content/public/browser/web_contents.h" 28 #include "extensions/browser/event_router.h" 29 #include "extensions/browser/extension_host.h" 30 #include "extensions/browser/extension_system.h" 31 #include "extensions/browser/lazy_background_task_queue.h" 32 #include "extensions/common/extension_set.h" 33 #include "extensions/common/manifest_handlers/background_info.h" 34 #include "net/base/escape.h" 35 #include "webkit/browser/fileapi/file_system_context.h" 36 #include "webkit/browser/fileapi/file_system_url.h" 37 #include "webkit/common/fileapi/file_system_info.h" 38 #include "webkit/common/fileapi/file_system_util.h" 39 40 using content::BrowserThread; 41 using content::ChildProcessSecurityPolicy; 42 using content::SiteInstance; 43 using content::WebContents; 44 using extensions::Extension; 45 using fileapi::FileSystemURL; 46 using file_manager::util::EntryDefinition; 47 using file_manager::util::EntryDefinitionList; 48 using file_manager::util::FileDefinition; 49 using file_manager::util::FileDefinitionList; 50 51 namespace file_manager { 52 namespace file_browser_handlers { 53 namespace { 54 55 // Returns process id of the process the extension is running in. 56 int ExtractProcessFromExtensionId(Profile* profile, 57 const std::string& extension_id) { 58 GURL extension_url = 59 Extension::GetBaseURLFromExtensionId(extension_id); 60 extensions::ProcessManager* manager = 61 extensions::ExtensionSystem::Get(profile)->process_manager(); 62 63 SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url); 64 if (!site_instance || !site_instance->HasProcess()) 65 return -1; 66 content::RenderProcessHost* process = site_instance->GetProcess(); 67 68 return process->GetID(); 69 } 70 71 // Finds a file browser handler that matches |action_id|. Returns NULL if not 72 // found. 73 const FileBrowserHandler* FindFileBrowserHandlerForActionId( 74 const Extension* extension, 75 const std::string& action_id) { 76 FileBrowserHandler::List* handler_list = 77 FileBrowserHandler::GetHandlers(extension); 78 for (FileBrowserHandler::List::const_iterator handler_iter = 79 handler_list->begin(); 80 handler_iter != handler_list->end(); 81 ++handler_iter) { 82 if (handler_iter->get()->id() == action_id) 83 return handler_iter->get(); 84 } 85 return NULL; 86 } 87 88 std::string EscapedUtf8ToLower(const std::string& str) { 89 base::string16 utf16 = base::UTF8ToUTF16( 90 net::UnescapeURLComponent(str, net::UnescapeRule::NORMAL)); 91 return net::EscapeUrlEncodedData( 92 base::UTF16ToUTF8(base::i18n::ToLower(utf16)), 93 false /* do not replace space with plus */); 94 } 95 96 // Finds file browser handlers that can handle the |selected_file_url|. 97 FileBrowserHandlerList FindFileBrowserHandlersForURL( 98 Profile* profile, 99 const GURL& selected_file_url) { 100 ExtensionService* service = 101 extensions::ExtensionSystem::Get(profile)->extension_service(); 102 // In unit-tests, we may not have an ExtensionService. 103 if (!service) 104 return FileBrowserHandlerList(); 105 106 // We need case-insensitive matching, and pattern in the handler is already 107 // in lower case. 108 const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec())); 109 110 FileBrowserHandlerList results; 111 for (extensions::ExtensionSet::const_iterator iter = 112 service->extensions()->begin(); 113 iter != service->extensions()->end(); ++iter) { 114 const Extension* extension = iter->get(); 115 if (profile->IsOffTheRecord() && 116 !extensions::util::IsIncognitoEnabled(extension->id(), profile)) 117 continue; 118 119 FileBrowserHandler::List* handler_list = 120 FileBrowserHandler::GetHandlers(extension); 121 if (!handler_list) 122 continue; 123 for (FileBrowserHandler::List::const_iterator handler_iter = 124 handler_list->begin(); 125 handler_iter != handler_list->end(); 126 ++handler_iter) { 127 const FileBrowserHandler* handler = handler_iter->get(); 128 if (!handler->MatchesURL(lowercase_url)) 129 continue; 130 131 results.push_back(handler_iter->get()); 132 } 133 } 134 return results; 135 } 136 137 // This class is used to execute a file browser handler task. Here's how this 138 // works: 139 // 140 // 1) Open the "external" file system 141 // 2) Set up permissions for the target files on the external file system. 142 // 3) Raise onExecute event with the action ID and entries of the target 143 // files. The event will launch the file browser handler if not active. 144 // 4) In the file browser handler, onExecute event is handled and executes the 145 // task in JavaScript. 146 // 147 // That said, the class itself does not execute a task. The task will be 148 // executed in JavaScript. 149 class FileBrowserHandlerExecutor { 150 public: 151 FileBrowserHandlerExecutor(Profile* profile, 152 const Extension* extension, 153 const std::string& action_id); 154 155 // Executes the task for each file. |done| will be run with the result. 156 void Execute(const std::vector<FileSystemURL>& file_urls, 157 const file_tasks::FileTaskFinishedCallback& done); 158 159 private: 160 // This object is responsible to delete itself. 161 virtual ~FileBrowserHandlerExecutor(); 162 163 // Checks legitimacy of file url and grants file RO access permissions from 164 // handler (target) extension and its renderer process. 165 static scoped_ptr<FileDefinitionList> SetupFileAccessPermissions( 166 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, 167 const scoped_refptr<const Extension>& handler_extension, 168 const std::vector<FileSystemURL>& file_urls); 169 170 void ExecuteDoneOnUIThread(bool success); 171 void ExecuteAfterSetupFileAccess(scoped_ptr<FileDefinitionList> file_list); 172 void ExecuteFileActionsOnUIThread( 173 scoped_ptr<FileDefinitionList> file_definition_list, 174 scoped_ptr<EntryDefinitionList> entry_definition_list); 175 void SetupPermissionsAndDispatchEvent( 176 scoped_ptr<FileDefinitionList> file_definition_list, 177 scoped_ptr<EntryDefinitionList> entry_definition_list, 178 int handler_pid_in, 179 extensions::ExtensionHost* host); 180 181 // Registers file permissions from |handler_host_permissions_| with 182 // ChildProcessSecurityPolicy for process with id |handler_pid|. 183 void SetupHandlerHostFileAccessPermissions( 184 FileDefinitionList* file_definition_list, 185 const Extension* extension, 186 int handler_pid); 187 188 Profile* profile_; 189 scoped_refptr<const Extension> extension_; 190 const std::string action_id_; 191 file_tasks::FileTaskFinishedCallback done_; 192 base::WeakPtrFactory<FileBrowserHandlerExecutor> weak_ptr_factory_; 193 194 DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerExecutor); 195 }; 196 197 // static 198 scoped_ptr<FileDefinitionList> 199 FileBrowserHandlerExecutor::SetupFileAccessPermissions( 200 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, 201 const scoped_refptr<const Extension>& handler_extension, 202 const std::vector<FileSystemURL>& file_urls) { 203 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 204 DCHECK(handler_extension.get()); 205 206 fileapi::ExternalFileSystemBackend* backend = 207 file_system_context_handler->external_backend(); 208 209 scoped_ptr<FileDefinitionList> file_definition_list(new FileDefinitionList); 210 for (size_t i = 0; i < file_urls.size(); ++i) { 211 const FileSystemURL& url = file_urls[i]; 212 213 // Check if this file system entry exists first. 214 base::File::Info file_info; 215 216 base::FilePath local_path = url.path(); 217 base::FilePath virtual_path = url.virtual_path(); 218 219 const bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive; 220 DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path)); 221 222 const bool is_native_file = 223 url.type() == fileapi::kFileSystemTypeNativeLocal || 224 url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal; 225 226 // If the file is from a physical volume, actual file must be found. 227 if (is_native_file) { 228 if (!base::PathExists(local_path) || 229 base::IsLink(local_path) || 230 !base::GetFileInfo(local_path, &file_info)) { 231 continue; 232 } 233 } 234 235 // Grant access to this particular file to target extension. This will 236 // ensure that the target extension can access only this FS entry and 237 // prevent from traversing FS hierarchy upward. 238 backend->GrantFileAccessToExtension(handler_extension->id(), virtual_path); 239 240 // Output values. 241 FileDefinition file_definition; 242 file_definition.virtual_path = virtual_path; 243 file_definition.is_directory = file_info.is_directory; 244 file_definition.absolute_path = local_path; 245 file_definition_list->push_back(file_definition); 246 } 247 248 return file_definition_list.Pass(); 249 } 250 251 FileBrowserHandlerExecutor::FileBrowserHandlerExecutor( 252 Profile* profile, 253 const Extension* extension, 254 const std::string& action_id) 255 : profile_(profile), 256 extension_(extension), 257 action_id_(action_id), 258 weak_ptr_factory_(this) { 259 } 260 261 FileBrowserHandlerExecutor::~FileBrowserHandlerExecutor() {} 262 263 void FileBrowserHandlerExecutor::Execute( 264 const std::vector<FileSystemURL>& file_urls, 265 const file_tasks::FileTaskFinishedCallback& done) { 266 done_ = done; 267 268 // Get file system context for the extension to which onExecute event will be 269 // sent. The file access permissions will be granted to the extension in the 270 // file system context for the files in |file_urls|. 271 scoped_refptr<fileapi::FileSystemContext> file_system_context( 272 util::GetFileSystemContextForExtensionId( 273 profile_, extension_->id())); 274 275 BrowserThread::PostTaskAndReplyWithResult( 276 BrowserThread::FILE, 277 FROM_HERE, 278 base::Bind(&SetupFileAccessPermissions, 279 file_system_context, 280 extension_, 281 file_urls), 282 base::Bind(&FileBrowserHandlerExecutor::ExecuteAfterSetupFileAccess, 283 weak_ptr_factory_.GetWeakPtr())); 284 } 285 286 void FileBrowserHandlerExecutor::ExecuteAfterSetupFileAccess( 287 scoped_ptr<FileDefinitionList> file_definition_list) { 288 // Outlives the conversion process, since bound to the callback. 289 const FileDefinitionList& file_definition_list_ref = 290 *file_definition_list.get(); 291 file_manager::util::ConvertFileDefinitionListToEntryDefinitionList( 292 profile_, 293 extension_->id(), 294 file_definition_list_ref, 295 base::Bind(&FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread, 296 weak_ptr_factory_.GetWeakPtr(), 297 base::Passed(&file_definition_list))); 298 } 299 300 void FileBrowserHandlerExecutor::ExecuteDoneOnUIThread(bool success) { 301 DCHECK_CURRENTLY_ON(BrowserThread::UI); 302 if (!done_.is_null()) 303 done_.Run( 304 success 305 ? extensions::api::file_browser_private::TASK_RESULT_MESSAGE_SENT 306 : extensions::api::file_browser_private::TASK_RESULT_FAILED); 307 delete this; 308 } 309 310 void FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread( 311 scoped_ptr<FileDefinitionList> file_definition_list, 312 scoped_ptr<EntryDefinitionList> entry_definition_list) { 313 DCHECK_CURRENTLY_ON(BrowserThread::UI); 314 315 if (file_definition_list->empty() || entry_definition_list->empty()) { 316 ExecuteDoneOnUIThread(false); 317 return; 318 } 319 320 int handler_pid = ExtractProcessFromExtensionId(profile_, extension_->id()); 321 if (handler_pid <= 0 && 322 !extensions::BackgroundInfo::HasLazyBackgroundPage(extension_.get())) { 323 ExecuteDoneOnUIThread(false); 324 return; 325 } 326 327 if (handler_pid > 0) { 328 SetupPermissionsAndDispatchEvent(file_definition_list.Pass(), 329 entry_definition_list.Pass(), 330 handler_pid, 331 NULL); 332 } else { 333 // We have to wake the handler background page before we proceed. 334 extensions::LazyBackgroundTaskQueue* queue = 335 extensions::ExtensionSystem::Get(profile_)-> 336 lazy_background_task_queue(); 337 if (!queue->ShouldEnqueueTask(profile_, extension_.get())) { 338 ExecuteDoneOnUIThread(false); 339 return; 340 } 341 queue->AddPendingTask( 342 profile_, 343 extension_->id(), 344 base::Bind( 345 &FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent, 346 weak_ptr_factory_.GetWeakPtr(), 347 base::Passed(file_definition_list.Pass()), 348 base::Passed(entry_definition_list.Pass()), 349 handler_pid)); 350 } 351 } 352 353 void FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent( 354 scoped_ptr<FileDefinitionList> file_definition_list, 355 scoped_ptr<EntryDefinitionList> entry_definition_list, 356 int handler_pid_in, 357 extensions::ExtensionHost* host) { 358 int handler_pid = host ? host->render_process_host()->GetID() : 359 handler_pid_in; 360 361 if (handler_pid <= 0) { 362 ExecuteDoneOnUIThread(false); 363 return; 364 } 365 366 extensions::EventRouter* router = extensions::EventRouter::Get(profile_); 367 if (!router) { 368 ExecuteDoneOnUIThread(false); 369 return; 370 } 371 372 SetupHandlerHostFileAccessPermissions( 373 file_definition_list.get(), extension_.get(), handler_pid); 374 375 scoped_ptr<base::ListValue> event_args(new base::ListValue()); 376 event_args->Append(new base::StringValue(action_id_)); 377 base::DictionaryValue* details = new base::DictionaryValue(); 378 event_args->Append(details); 379 // Get file definitions. These will be replaced with Entry instances by 380 // dispatchEvent() method from event_binding.js. 381 base::ListValue* file_entries = new base::ListValue(); 382 details->Set("entries", file_entries); 383 384 for (EntryDefinitionList::const_iterator iter = 385 entry_definition_list->begin(); 386 iter != entry_definition_list->end(); 387 ++iter) { 388 base::DictionaryValue* file_def = new base::DictionaryValue(); 389 file_entries->Append(file_def); 390 file_def->SetString("fileSystemName", iter->file_system_name); 391 file_def->SetString("fileSystemRoot", iter->file_system_root_url); 392 file_def->SetString("fileFullPath", 393 "/" + iter->full_path.AsUTF8Unsafe()); 394 file_def->SetBoolean("fileIsDirectory", iter->is_directory); 395 } 396 397 scoped_ptr<extensions::Event> event(new extensions::Event( 398 "fileBrowserHandler.onExecute", event_args.Pass())); 399 event->restrict_to_browser_context = profile_; 400 router->DispatchEventToExtension(extension_->id(), event.Pass()); 401 402 ExecuteDoneOnUIThread(true); 403 } 404 405 void FileBrowserHandlerExecutor::SetupHandlerHostFileAccessPermissions( 406 FileDefinitionList* file_definition_list, 407 const Extension* extension, 408 int handler_pid) { 409 const FileBrowserHandler* action = FindFileBrowserHandlerForActionId( 410 extension_, action_id_); 411 for (FileDefinitionList::const_iterator iter = file_definition_list->begin(); 412 iter != file_definition_list->end(); 413 ++iter) { 414 if (!action) 415 continue; 416 if (action->CanRead()) { 417 content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile( 418 handler_pid, iter->absolute_path); 419 } 420 if (action->CanWrite()) { 421 content::ChildProcessSecurityPolicy::GetInstance()-> 422 GrantCreateReadWriteFile(handler_pid, iter->absolute_path); 423 } 424 } 425 } 426 427 // Returns true if |extension_id| and |action_id| indicate that the file 428 // currently being handled should be opened with the browser. This function 429 // is used to handle certain action IDs of the file manager. 430 bool ShouldBeOpenedWithBrowser(const std::string& extension_id, 431 const std::string& action_id) { 432 433 return (extension_id == kFileManagerAppId && 434 (action_id == "view-pdf" || 435 action_id == "view-swf" || 436 action_id == "view-in-browser" || 437 action_id == "open-hosted-generic" || 438 action_id == "open-hosted-gdoc" || 439 action_id == "open-hosted-gsheet" || 440 action_id == "open-hosted-gslides")); 441 } 442 443 // Opens the files specified by |file_urls| with the browser for |profile|. 444 // Returns true on success. It's a failure if no files are opened. 445 bool OpenFilesWithBrowser(Profile* profile, 446 const std::vector<FileSystemURL>& file_urls) { 447 int num_opened = 0; 448 for (size_t i = 0; i < file_urls.size(); ++i) { 449 const FileSystemURL& file_url = file_urls[i]; 450 if (chromeos::FileSystemBackend::CanHandleURL(file_url)) { 451 const base::FilePath& file_path = file_url.path(); 452 num_opened += util::OpenFileWithBrowser(profile, file_path); 453 } 454 } 455 return num_opened > 0; 456 } 457 458 } // namespace 459 460 bool ExecuteFileBrowserHandler( 461 Profile* profile, 462 const Extension* extension, 463 const std::string& action_id, 464 const std::vector<FileSystemURL>& file_urls, 465 const file_tasks::FileTaskFinishedCallback& done) { 466 // Forbid calling undeclared handlers. 467 if (!FindFileBrowserHandlerForActionId(extension, action_id)) 468 return false; 469 470 // Some action IDs of the file manager's file browser handlers require the 471 // files to be directly opened with the browser. 472 if (ShouldBeOpenedWithBrowser(extension->id(), action_id)) { 473 const bool result = OpenFilesWithBrowser(profile, file_urls); 474 if (result && !done.is_null()) 475 done.Run(extensions::api::file_browser_private::TASK_RESULT_OPENED); 476 return result; 477 } 478 479 // The executor object will be self deleted on completion. 480 (new FileBrowserHandlerExecutor( 481 profile, extension, action_id))->Execute(file_urls, done); 482 return true; 483 } 484 485 bool IsFallbackFileBrowserHandler(const file_tasks::TaskDescriptor& task) { 486 return ((task.task_type == file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER || 487 task.task_type == file_tasks::TASK_TYPE_FILE_HANDLER) && 488 (task.app_id == kFileManagerAppId || 489 task.app_id == kVideoPlayerAppId || 490 task.app_id == kGalleryAppId || 491 task.app_id == extension_misc::kQuickOfficeComponentExtensionId || 492 task.app_id == extension_misc::kQuickOfficeInternalExtensionId || 493 task.app_id == extension_misc::kQuickOfficeExtensionId)); 494 } 495 496 FileBrowserHandlerList FindFileBrowserHandlers( 497 Profile* profile, 498 const std::vector<GURL>& file_list) { 499 FileBrowserHandlerList common_handlers; 500 for (std::vector<GURL>::const_iterator it = file_list.begin(); 501 it != file_list.end(); ++it) { 502 FileBrowserHandlerList handlers = 503 FindFileBrowserHandlersForURL(profile, *it); 504 // If there is nothing to do for one file, the intersection of handlers 505 // for all files will be empty at the end, so no need to check further. 506 if (handlers.empty()) 507 return FileBrowserHandlerList(); 508 509 // For the very first file, just copy all the elements. 510 if (it == file_list.begin()) { 511 common_handlers = handlers; 512 } else { 513 // For all additional files, find intersection between the accumulated and 514 // file specific set. 515 FileBrowserHandlerList intersection; 516 std::set<const FileBrowserHandler*> common_handler_set( 517 common_handlers.begin(), common_handlers.end()); 518 519 for (FileBrowserHandlerList::const_iterator itr = handlers.begin(); 520 itr != handlers.end(); ++itr) { 521 if (ContainsKey(common_handler_set, *itr)) 522 intersection.push_back(*itr); 523 } 524 525 std::swap(common_handlers, intersection); 526 if (common_handlers.empty()) 527 return FileBrowserHandlerList(); 528 } 529 } 530 531 return common_handlers; 532 } 533 534 } // namespace file_browser_handlers 535 } // namespace file_manager 536